From d9724597eded17784b395b324d653305f3bf0e90 Mon Sep 17 00:00:00 2001 From: Jared Popelar Date: Wed, 27 Sep 2023 09:58:12 -0600 Subject: 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 --- var/spack/repos/builtin/packages/boost/package.py | 39 ++++++---- .../builtin/packages/openblas/ifort-msvc.patch | 13 ++++ .../repos/builtin/packages/openblas/package.py | 90 +++++++++++++++------- var/spack/repos/builtin/packages/pcre/package.py | 26 ++++++- .../repos/builtin/packages/superlu/package.py | 2 + .../repos/builtin/packages/trilinos/package.py | 19 ++++- 6 files changed, 145 insertions(+), 44 deletions(-) create mode 100644 var/spack/repos/builtin/packages/openblas/ifort-msvc.patch diff --git a/var/spack/repos/builtin/packages/boost/package.py b/var/spack/repos/builtin/packages/boost/package.py index efa8b2270b..58b836ac60 100644 --- a/var/spack/repos/builtin/packages/boost/package.py +++ b/var/spack/repos/builtin/packages/boost/package.py @@ -498,7 +498,9 @@ class Boost(Package): with open("user-config.jam", "w") as f: # Boost may end up using gcc even though clang+gfortran is set in # 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: # Use the correct mpi compiler. If the compiler options are @@ -584,7 +586,7 @@ class Boost(Package): 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. # Earlier versions could not specify Intel as the toolset # as that was considered to be redundant/conflicting with @@ -634,7 +636,7 @@ class Boost(Package): return threading_opts 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): if os.path.isfile(lib): prefix, remainder = lib.split(".", 1) @@ -687,12 +689,15 @@ class Boost(Package): # to make Boost find the user-config.jam env["BOOST_BUILD_PATH"] = self.stage.source_path - bootstrap = Executable("./bootstrap.sh") - bootstrap_options = ["--prefix=%s" % prefix] 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 # user-config being overwritten (again intel, but different boost version) @@ -704,6 +709,8 @@ class Boost(Package): # b2 used to be called bjam, before 1.47 (sigh) 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) jobs = make_jobs @@ -711,11 +718,14 @@ class Boost(Package): if jobs > 64 and spec.satisfies("@:1.58"): jobs = 64 - b2_options = [ - "-j", - "%s" % jobs, - "--user-config=%s" % os.path.join(self.stage.source_path, "user-config.jam"), - ] + # Windows just wants a b2 call with no args + b2_options = [] + if not self.spec.satisfies("platform=windows"): + 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) @@ -727,8 +737,11 @@ class Boost(Package): # In theory it could be done on one call but it fails on # Boost.MPI if the threading options are not separated. - for threading_opt in threading_opts: - b2("install", "threading=%s" % threading_opt, *b2_options) + if not self.spec.satisfies("platform=windows"): + 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: self.add_buildopt_symlinks(prefix) diff --git a/var/spack/repos/builtin/packages/openblas/ifort-msvc.patch b/var/spack/repos/builtin/packages/openblas/ifort-msvc.patch new file mode 100644 index 0000000000..dd4c97fdd8 --- /dev/null +++ b/var/spack/repos/builtin/packages/openblas/ifort-msvc.patch @@ -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 () diff --git a/var/spack/repos/builtin/packages/openblas/package.py b/var/spack/repos/builtin/packages/openblas/package.py index 8b00d2983c..e194a366e4 100644 --- a/var/spack/repos/builtin/packages/openblas/package.py +++ b/var/spack/repos/builtin/packages/openblas/package.py @@ -6,11 +6,13 @@ import os import re +import spack.build_systems.cmake +import spack.build_systems.makefile from spack.package import * from spack.package_test import compare_output_file, compile_c_and_execute -class Openblas(MakefilePackage): +class Openblas(CMakePackage, MakefilePackage): """OpenBLAS: An optimized BLAS library""" homepage = "https://www.openblas.net" @@ -19,7 +21,7 @@ class Openblas(MakefilePackage): ) git = "https://github.com/OpenMathLib/OpenBLAS.git" - libraries = ["libopenblas"] + libraries = ["libopenblas", "openblas"] version("develop", branch="develop") 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.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 patch("cce.patch", when="@0.3.20 %cce") @@ -213,6 +218,8 @@ class Openblas(MakefilePackage): depends_on("perl", type="build") + build_system("makefile", "cmake", default="makefile") + def flag_handler(self, name, flags): spec = self.spec iflags = [] @@ -242,13 +249,37 @@ class Openblas(MakefilePackage): # require both. # As of 08/2022 (0.3.21), we can build purely with a C compiler using # 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: raise InstallError( self.compiler.cc + " 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 def _read_targets(target_file): """Parse a list of available targets from the OpenBLAS/TargetList.txt @@ -304,7 +335,7 @@ class Openblas(MakefilePackage): if microarch.name in available_targets: break - if self.version >= Version("0.3"): + if self.spec.version >= Version("0.3"): # 'ARCH' argument causes build errors in older OpenBLAS # see https://github.com/spack/spack/issues/15385 arch_name = microarch.family.name @@ -379,9 +410,9 @@ class Openblas(MakefilePackage): if "~shared" 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: - 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"] # fix missing _dggsvd_ and _sggsvd_ if self.spec.satisfies("@0.2.16"): @@ -442,28 +473,6 @@ class Openblas(MakefilePackage): 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 def build_targets(self): return ["-s"] + self.make_defs + ["all"] @@ -499,3 +508,28 @@ class Openblas(MakefilePackage): output = compile_c_and_execute(source_file, [include_flags], link_flags.split()) 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 diff --git a/var/spack/repos/builtin/packages/pcre/package.py b/var/spack/repos/builtin/packages/pcre/package.py index 4e6e047335..4719f5ea37 100644 --- a/var/spack/repos/builtin/packages/pcre/package.py +++ b/var/spack/repos/builtin/packages/pcre/package.py @@ -3,10 +3,12 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import spack.build_systems.autotools +import spack.build_systems.cmake from spack.package import * -class Pcre(AutotoolsPackage): +class Pcre(AutotoolsPackage, CMakePackage): """The PCRE package contains Perl Compatible Regular Expression libraries. These are useful for implementing regular expression pattern matching using the same syntax and semantics as Perl 5.""" @@ -26,6 +28,8 @@ class Pcre(AutotoolsPackage): maintainers("drkennetz") patch("intel.patch", when="@8.38") + build_system("autotools", "cmake", default="autotools") + variant("jit", default=False, description="Enable JIT support.") 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.", ) + +class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder): def configure_args(self): args = [] @@ -51,3 +57,21 @@ class Pcre(AutotoolsPackage): args.append("--enable-unicode-properties") 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 diff --git a/var/spack/repos/builtin/packages/superlu/package.py b/var/spack/repos/builtin/packages/superlu/package.py index fd7f66d4d0..9ad86ab219 100644 --- a/var/spack/repos/builtin/packages/superlu/package.py +++ b/var/spack/repos/builtin/packages/superlu/package.py @@ -46,6 +46,8 @@ class Superlu(CMakePackage, Package): 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") depends_on("blas") diff --git a/var/spack/repos/builtin/packages/trilinos/package.py b/var/spack/repos/builtin/packages/trilinos/package.py index 20a8d290e2..28f68b1b48 100644 --- a/var/spack/repos/builtin/packages/trilinos/package.py +++ b/var/spack/repos/builtin/packages/trilinos/package.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) import os +import pathlib import sys from spack.build_environment import dso_suffix @@ -366,6 +367,18 @@ class Trilinos(CMakePackage, CudaPackage, ROCmPackage): conflicts("+stokhos", when="%xl") 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 ########################## # External Kokkos @@ -393,7 +406,9 @@ class Trilinos(CMakePackage, CudaPackage, ROCmPackage): depends_on("cgns", when="+exodus") depends_on("cmake@3.23:", type="build", when="@14.0.0:") 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("lapack") # depends_on('perl', type=('build',)) # TriBITS finds but doesn't use... @@ -811,7 +826,7 @@ class Trilinos(CMakePackage, CudaPackage, ROCmPackage): define("CMAKE_C_COMPILER", spec["mpi"].mpicc), define("CMAKE_CXX_COMPILER", spec["mpi"].mpicxx), define("CMAKE_Fortran_COMPILER", spec["mpi"].mpifc), - define("MPI_BASE_DIR", spec["mpi"].prefix), + define("MPI_BASE_DIR", str(pathlib.PurePosixPath(spec["mpi"].prefix))), ] ) -- cgit v1.2.3-70-g09d2