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:
# 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 @@ def determine_b2_options(self, spec, options):
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 @@ def determine_b2_options(self, spec, options):
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 @@ def install(self, spec, prefix):
# 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 @@ def install(self, spec, prefix):
# 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 @@ def install(self, spec, prefix):
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 @@ def install(self, spec, prefix):
# 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)

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 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 @@ def check_compilers(self):
# 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 @@ def _microarch_target_args(self):
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 @@ def make_defs(self):
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 @@ def make_defs(self):
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 @@ def check_install(self):
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

View File

@ -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 @@ def configure_args(self):
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

View File

@ -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")

View File

@ -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 @@ def define_tpl(trilinos_name, spack_name, have_dep):
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))),
]
)