Trilinos package: build on Windows (#34622)

Update Trilinos and dependencies to build a limited version of Trilinos
on Windows.

* Support trilinos~mpi~shared on Windows
* superlu: force CMake build on Windows
* boost: update to build on Windows (proper option formatting and
  build tool names)
* pcre, openblas: add CMake-based build (keep prior build system
  as default on platforms other than Windows)
* openblas: add patch when using Intel Fortran compiler (currently
  this is included as part of the hybrid %msvc compiler in Spack)

Co-authored-by: John Parent <john.parent@kitware.com>
This commit is contained in:
Jared Popelar 2023-09-27 09:58:12 -06:00 committed by GitHub
parent c90c946d52
commit d9724597ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 44 deletions

View File

@ -498,7 +498,9 @@ def determine_bootstrap_options(self, spec, with_libs, options):
with open("user-config.jam", "w") as f: with open("user-config.jam", "w") as f:
# Boost may end up using gcc even though clang+gfortran is set in # Boost may end up using gcc even though clang+gfortran is set in
# compilers.yaml. Make sure this does not happen: # compilers.yaml. Make sure this does not happen:
f.write("using {0} : : {1} ;\n".format(boost_toolset_id, spack_cxx)) if not spec.satisfies("platform=windows"):
# Skip this on Windows since we don't have a cl.exe wrapper in spack
f.write("using {0} : : {1} ;\n".format(boost_toolset_id, spack_cxx))
if "+mpi" in spec: if "+mpi" in spec:
# Use the correct mpi compiler. If the compiler options are # Use the correct mpi compiler. If the compiler options are
@ -584,7 +586,7 @@ def determine_b2_options(self, spec, options):
options.extend(["link=%s" % ",".join(link_types), "--layout=%s" % layout]) options.extend(["link=%s" % ",".join(link_types), "--layout=%s" % layout])
if not spec.satisfies("@:1.75 %intel"): if not spec.satisfies("@:1.75 %intel") and not spec.satisfies("platform=windows"):
# When building any version >= 1.76, the toolset must be specified. # When building any version >= 1.76, the toolset must be specified.
# Earlier versions could not specify Intel as the toolset # Earlier versions could not specify Intel as the toolset
# as that was considered to be redundant/conflicting with # as that was considered to be redundant/conflicting with
@ -634,7 +636,7 @@ def determine_b2_options(self, spec, options):
return threading_opts return threading_opts
def add_buildopt_symlinks(self, prefix): def add_buildopt_symlinks(self, prefix):
with working_dir(prefix.lib): with working_dir(prefix.lib, create=True):
for lib in os.listdir(os.curdir): for lib in os.listdir(os.curdir):
if os.path.isfile(lib): if os.path.isfile(lib):
prefix, remainder = lib.split(".", 1) prefix, remainder = lib.split(".", 1)
@ -687,12 +689,15 @@ def install(self, spec, prefix):
# to make Boost find the user-config.jam # to make Boost find the user-config.jam
env["BOOST_BUILD_PATH"] = self.stage.source_path env["BOOST_BUILD_PATH"] = self.stage.source_path
bootstrap = Executable("./bootstrap.sh")
bootstrap_options = ["--prefix=%s" % prefix] bootstrap_options = ["--prefix=%s" % prefix]
self.determine_bootstrap_options(spec, with_libs, bootstrap_options) self.determine_bootstrap_options(spec, with_libs, bootstrap_options)
bootstrap(*bootstrap_options) if self.spec.satisfies("platform=windows"):
bootstrap = Executable("cmd.exe")
bootstrap("/c", ".\\bootstrap.bat", *bootstrap_options)
else:
bootstrap = Executable("./bootstrap.sh")
bootstrap(*bootstrap_options)
# strip the toolchain to avoid double include errors (intel) or # strip the toolchain to avoid double include errors (intel) or
# user-config being overwritten (again intel, but different boost version) # user-config being overwritten (again intel, but different boost version)
@ -704,6 +709,8 @@ def install(self, spec, prefix):
# b2 used to be called bjam, before 1.47 (sigh) # b2 used to be called bjam, before 1.47 (sigh)
b2name = "./b2" if spec.satisfies("@1.47:") else "./bjam" b2name = "./b2" if spec.satisfies("@1.47:") else "./bjam"
if self.spec.satisfies("platform=windows"):
b2name = "b2.exe" if spec.satisfies("@1.47:") else "bjam.exe"
b2 = Executable(b2name) b2 = Executable(b2name)
jobs = make_jobs jobs = make_jobs
@ -711,11 +718,14 @@ def install(self, spec, prefix):
if jobs > 64 and spec.satisfies("@:1.58"): if jobs > 64 and spec.satisfies("@:1.58"):
jobs = 64 jobs = 64
b2_options = [ # Windows just wants a b2 call with no args
"-j", b2_options = []
"%s" % jobs, if not self.spec.satisfies("platform=windows"):
"--user-config=%s" % os.path.join(self.stage.source_path, "user-config.jam"), path_to_config = "--user-config=%s" % os.path.join(
] self.stage.source_path, "user-config.jam"
)
b2_options = ["-j", "%s" % jobs]
b2_options.append(path_to_config)
threading_opts = self.determine_b2_options(spec, b2_options) threading_opts = self.determine_b2_options(spec, b2_options)
@ -727,8 +737,11 @@ def install(self, spec, prefix):
# In theory it could be done on one call but it fails on # In theory it could be done on one call but it fails on
# Boost.MPI if the threading options are not separated. # Boost.MPI if the threading options are not separated.
for threading_opt in threading_opts: if not self.spec.satisfies("platform=windows"):
b2("install", "threading=%s" % threading_opt, *b2_options) for threading_opt in threading_opts:
b2("install", "threading=%s" % threading_opt, *b2_options)
else:
b2("install", *b2_options)
if "+multithreaded" in spec and "~taggedlayout" in spec: if "+multithreaded" in spec and "~taggedlayout" in spec:
self.add_buildopt_symlinks(prefix) self.add_buildopt_symlinks(prefix)

View File

@ -0,0 +1,13 @@
diff -ruN spack-src/cmake/fc.cmake spack-src-new/cmake/fc.cmake
--- spack-src/cmake/fc.cmake 2023-04-01 14:18:01.000000000 -0600
+++ spack-src-new/cmake/fc.cmake 2023-06-06 09:34:12.921982500 -0600
@@ -89,6 +89,9 @@
if (${F_COMPILER} STREQUAL "INTEL")
set(CCOMMON_OPT "${CCOMMON_OPT} -DF_INTERFACE_INTEL")
+ if (MSVC)
+ set(FCOMMON_OPT "${FCOMMON_OPT} -names:uppercase -assume:underscore")
+ endif ()
if (INTERFACE64)
set(FCOMMON_OPT "${FCOMMON_OPT} -i8")
endif ()

View File

@ -6,11 +6,13 @@
import os import os
import re import re
import spack.build_systems.cmake
import spack.build_systems.makefile
from spack.package import * from spack.package import *
from spack.package_test import compare_output_file, compile_c_and_execute from spack.package_test import compare_output_file, compile_c_and_execute
class Openblas(MakefilePackage): class Openblas(CMakePackage, MakefilePackage):
"""OpenBLAS: An optimized BLAS library""" """OpenBLAS: An optimized BLAS library"""
homepage = "https://www.openblas.net" homepage = "https://www.openblas.net"
@ -19,7 +21,7 @@ class Openblas(MakefilePackage):
) )
git = "https://github.com/OpenMathLib/OpenBLAS.git" git = "https://github.com/OpenMathLib/OpenBLAS.git"
libraries = ["libopenblas"] libraries = ["libopenblas", "openblas"]
version("develop", branch="develop") version("develop", branch="develop")
version("0.3.24", sha256="ceadc5065da97bd92404cac7254da66cc6eb192679cf1002098688978d4d5132") version("0.3.24", sha256="ceadc5065da97bd92404cac7254da66cc6eb192679cf1002098688978d4d5132")
@ -91,6 +93,9 @@ class Openblas(MakefilePackage):
provides("lapack@3.9.1:", when="@0.3.15:") provides("lapack@3.9.1:", when="@0.3.15:")
provides("lapack@3.7.0", when="@0.2.20") provides("lapack@3.7.0", when="@0.2.20")
# https://github.com/xianyi/OpenBLAS/pull/2519/files
patch("ifort-msvc.patch", when="%msvc")
# https://github.com/OpenMathLib/OpenBLAS/pull/3712 # https://github.com/OpenMathLib/OpenBLAS/pull/3712
patch("cce.patch", when="@0.3.20 %cce") patch("cce.patch", when="@0.3.20 %cce")
@ -213,6 +218,8 @@ class Openblas(MakefilePackage):
depends_on("perl", type="build") depends_on("perl", type="build")
build_system("makefile", "cmake", default="makefile")
def flag_handler(self, name, flags): def flag_handler(self, name, flags):
spec = self.spec spec = self.spec
iflags = [] iflags = []
@ -242,13 +249,37 @@ def check_compilers(self):
# require both. # require both.
# As of 08/2022 (0.3.21), we can build purely with a C compiler using # As of 08/2022 (0.3.21), we can build purely with a C compiler using
# a f2c translated LAPACK version # a f2c translated LAPACK version
# https://github.com/OpenMathLib/OpenBLAS/releases/tag/v0.3.21 # https://github.com/xianyi/OpenBLAS/releases/tag/v0.3.21
if self.compiler.fc is None and "~fortran" not in self.spec: if self.compiler.fc is None and "~fortran" not in self.spec:
raise InstallError( raise InstallError(
self.compiler.cc self.compiler.cc
+ " has no Fortran compiler added in spack. Add it or use openblas~fortran!" + " has no Fortran compiler added in spack. Add it or use openblas~fortran!"
) )
@property
def headers(self):
# The only public headers for cblas and lapacke in
# openblas are cblas.h and lapacke.h. The remaining headers are private
# headers either included in one of these two headers, or included in
# one of the source files implementing functions declared in these
# headers.
return find_headers(["cblas", "lapacke"], self.prefix.include)
@property
def libs(self):
spec = self.spec
# Look for openblas{symbol_suffix}
name = ["libopenblas", "openblas"]
search_shared = bool(spec.variants["shared"].value)
suffix = spec.variants["symbol_suffix"].value
if suffix != "none":
name += suffix
return find_libraries(name, spec.prefix, shared=search_shared, recursive=True)
class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder):
@staticmethod @staticmethod
def _read_targets(target_file): def _read_targets(target_file):
"""Parse a list of available targets from the OpenBLAS/TargetList.txt """Parse a list of available targets from the OpenBLAS/TargetList.txt
@ -304,7 +335,7 @@ def _microarch_target_args(self):
if microarch.name in available_targets: if microarch.name in available_targets:
break break
if self.version >= Version("0.3"): if self.spec.version >= Version("0.3"):
# 'ARCH' argument causes build errors in older OpenBLAS # 'ARCH' argument causes build errors in older OpenBLAS
# see https://github.com/spack/spack/issues/15385 # see https://github.com/spack/spack/issues/15385
arch_name = microarch.family.name arch_name = microarch.family.name
@ -379,9 +410,9 @@ def make_defs(self):
if "~shared" in self.spec: if "~shared" in self.spec:
if "+pic" in self.spec: if "+pic" in self.spec:
make_defs.append("CFLAGS={0}".format(self.compiler.cc_pic_flag)) make_defs.append("CFLAGS={0}".format(self.pkg.compiler.cc_pic_flag))
if "~fortran" not in self.spec: if "~fortran" not in self.spec:
make_defs.append("FFLAGS={0}".format(self.compiler.f77_pic_flag)) make_defs.append("FFLAGS={0}".format(self.pkg.compiler.f77_pic_flag))
make_defs += ["NO_SHARED=1"] make_defs += ["NO_SHARED=1"]
# fix missing _dggsvd_ and _sggsvd_ # fix missing _dggsvd_ and _sggsvd_
if self.spec.satisfies("@0.2.16"): if self.spec.satisfies("@0.2.16"):
@ -442,28 +473,6 @@ def make_defs(self):
return make_defs return make_defs
@property
def headers(self):
# As in netlib-lapack, the only public headers for cblas and lapacke in
# openblas are cblas.h and lapacke.h. The remaining headers are private
# headers either included in one of these two headers, or included in
# one of the source files implementing functions declared in these
# headers.
return find_headers(["cblas", "lapacke"], self.prefix.include)
@property
def libs(self):
spec = self.spec
# Look for openblas{symbol_suffix}
name = "libopenblas"
search_shared = bool(spec.variants["shared"].value)
suffix = spec.variants["symbol_suffix"].value
if suffix != "none":
name += suffix
return find_libraries(name, spec.prefix, shared=search_shared, recursive=True)
@property @property
def build_targets(self): def build_targets(self):
return ["-s"] + self.make_defs + ["all"] return ["-s"] + self.make_defs + ["all"]
@ -499,3 +508,28 @@ def check_install(self):
output = compile_c_and_execute(source_file, [include_flags], link_flags.split()) output = compile_c_and_execute(source_file, [include_flags], link_flags.split())
compare_output_file(output, blessed_file) compare_output_file(output, blessed_file)
class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
def cmake_args(self):
cmake_defs = [self.define("TARGET", "GENERIC")]
if self.spec.satisfies("platform=windows"):
cmake_defs += [
self.define("DYNAMIC_ARCH", "OFF"),
self.define("BUILD_WITHOUT_LAPACK", "ON"),
]
if "~fortran" in self.spec:
cmake_defs += [self.define("NOFORTRAN", "ON")]
if "+shared" in self.spec:
cmake_defs += [self.define("BUILD_SHARED_LIBS", "ON")]
if self.spec.satisfies("threads=openmp"):
cmake_defs += [self.define("USE_OPENMP", "ON"), self.define("USE_THREAD", "ON")]
elif self.spec.satisfies("threads=pthreads"):
cmake_defs += [self.define("USE_OPENMP", "OFF"), self.define("USE_THREAD", "ON")]
else:
cmake_defs += [self.define("USE_OPENMP", "OFF"), self.define("USE_THREAD", "OFF")]
return cmake_defs

View File

@ -3,10 +3,12 @@
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import spack.build_systems.autotools
import spack.build_systems.cmake
from spack.package import * from spack.package import *
class Pcre(AutotoolsPackage): class Pcre(AutotoolsPackage, CMakePackage):
"""The PCRE package contains Perl Compatible Regular Expression """The PCRE package contains Perl Compatible Regular Expression
libraries. These are useful for implementing regular expression libraries. These are useful for implementing regular expression
pattern matching using the same syntax and semantics as Perl 5.""" pattern matching using the same syntax and semantics as Perl 5."""
@ -26,6 +28,8 @@ class Pcre(AutotoolsPackage):
maintainers("drkennetz") maintainers("drkennetz")
patch("intel.patch", when="@8.38") patch("intel.patch", when="@8.38")
build_system("autotools", "cmake", default="autotools")
variant("jit", default=False, description="Enable JIT support.") variant("jit", default=False, description="Enable JIT support.")
variant("multibyte", default=True, description="Enable support for 16 and 32 bit characters.") variant("multibyte", default=True, description="Enable support for 16 and 32 bit characters.")
@ -36,6 +40,8 @@ class Pcre(AutotoolsPackage):
description="Enable support for UTF-8/16/32, " "incompatible with EBCDIC.", description="Enable support for UTF-8/16/32, " "incompatible with EBCDIC.",
) )
class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder):
def configure_args(self): def configure_args(self):
args = [] args = []
@ -51,3 +57,21 @@ def configure_args(self):
args.append("--enable-unicode-properties") args.append("--enable-unicode-properties")
return args return args
class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
def cmake_args(self):
args = []
if "+jit" in self.spec:
args.append("-DPCRE_SUPPORT_JIT:BOOL=ON")
if "+multibyte" in self.spec:
args.append("-DPCRE_BUILD_PCRE16:BOOL=ON")
args.append("-DPCRE_BUILD_PCRE32:BOOL=ON")
if "+utf" in self.spec:
args.append("-DPCRE_SUPPORT_UTF:BOOL=ON")
args.append("-DPCRE_SUPPORT_UNICODE_PROPERTIES:BOOL=ON")
return args

View File

@ -46,6 +46,8 @@ class Superlu(CMakePackage, Package):
conditional("cmake", when="@5:"), conditional("generic", when="@:4"), default="cmake" conditional("cmake", when="@5:"), conditional("generic", when="@:4"), default="cmake"
) )
requires("build_system=cmake", when="platform=windows")
variant("pic", default=True, description="Build with position independent code") variant("pic", default=True, description="Build with position independent code")
depends_on("blas") depends_on("blas")

View File

@ -4,6 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os import os
import pathlib
import sys import sys
from spack.build_environment import dso_suffix from spack.build_environment import dso_suffix
@ -366,6 +367,18 @@ class Trilinos(CMakePackage, CudaPackage, ROCmPackage):
conflicts("+stokhos", when="%xl") conflicts("+stokhos", when="%xl")
conflicts("+stokhos", when="%xl_r") conflicts("+stokhos", when="%xl_r")
# Current Windows support, only have serial static builds
conflicts(
"+shared",
when="platform=windows",
msg="Only static builds are supported on Windows currently.",
)
conflicts(
"+mpi",
when="platform=windows",
msg="Only serial builds are supported on Windows currently.",
)
# ###################### Dependencies ########################## # ###################### Dependencies ##########################
# External Kokkos # External Kokkos
@ -393,7 +406,9 @@ class Trilinos(CMakePackage, CudaPackage, ROCmPackage):
depends_on("cgns", when="+exodus") depends_on("cgns", when="+exodus")
depends_on("cmake@3.23:", type="build", when="@14.0.0:") depends_on("cmake@3.23:", type="build", when="@14.0.0:")
depends_on("hdf5+hl", when="+hdf5") depends_on("hdf5+hl", when="+hdf5")
depends_on("hypre~internal-superlu~int64", when="+hypre") for plat in ["cray", "darwin", "linux"]:
depends_on("hypre~internal-superlu~int64", when="+hypre platform=%s" % plat)
depends_on("hypre-cmake~int64", when="+hypre platform=windows")
depends_on("kokkos-nvcc-wrapper", when="+wrapper") depends_on("kokkos-nvcc-wrapper", when="+wrapper")
depends_on("lapack") depends_on("lapack")
# depends_on('perl', type=('build',)) # TriBITS finds but doesn't use... # depends_on('perl', type=('build',)) # TriBITS finds but doesn't use...
@ -811,7 +826,7 @@ def define_tpl(trilinos_name, spack_name, have_dep):
define("CMAKE_C_COMPILER", spec["mpi"].mpicc), define("CMAKE_C_COMPILER", spec["mpi"].mpicc),
define("CMAKE_CXX_COMPILER", spec["mpi"].mpicxx), define("CMAKE_CXX_COMPILER", spec["mpi"].mpicxx),
define("CMAKE_Fortran_COMPILER", spec["mpi"].mpifc), define("CMAKE_Fortran_COMPILER", spec["mpi"].mpifc),
define("MPI_BASE_DIR", spec["mpi"].prefix), define("MPI_BASE_DIR", str(pathlib.PurePosixPath(spec["mpi"].prefix))),
] ]
) )