boost package: fix Windows build (#43732)

* Boost:Adjust bootstrapping/b2 options as needed for Windows (the
  bootstrapping phase sufficiently differs between Windows/Unix
  that it is handled entirely within its own branch).
* Boost: Paths in user-config.jam should be POSIX, including on Windows
* Python: `.libs` for the Python package should return link libraries
  on Windows. The libraries are also stored in a different directory.
This commit is contained in:
James Smillie 2024-09-11 01:43:28 -06:00 committed by GitHub
parent e1da0a7312
commit fc4a4ec70d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 26 deletions

View File

@ -5,6 +5,7 @@
import os
import sys
from pathlib import Path
from spack.package import *
@ -303,6 +304,11 @@ def libs(self):
# safe to do so on affected platforms.
conflicts("+clanglibcpp", when="@1.85: +stacktrace")
# On Windows, the signals variant is required when building any of
# the all_libs variants.
for lib in all_libs:
requires("+signals", when=f"+{lib} platform=windows")
# Patch fix from https://svn.boost.org/trac/boost/ticket/11856
patch("boost_11856.patch", when="@1.60.0%gcc@4.4.7")
@ -495,9 +501,9 @@ def bjam_python_line(self, spec):
return "using python : {0} : {1} : {2} : {3} ;\n".format(
spec["python"].version.up_to(2),
spec["python"].command.path,
spec["python"].headers.directories[0],
spec["python"].libs[0],
Path(spec["python"].command.path).as_posix(),
Path(spec["python"].headers.directories[0]).as_posix(),
Path(spec["python"].libs[0]).parent.as_posix(),
)
def determine_bootstrap_options(self, spec, with_libs, options):
@ -521,6 +527,9 @@ def determine_bootstrap_options(self, spec, with_libs, options):
else:
options.append("--without-icu")
self.write_jam_file(spec, boost_toolset_id)
def write_jam_file(self, spec, boost_toolset_id=None):
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:
@ -535,7 +544,7 @@ def determine_bootstrap_options(self, spec, with_libs, options):
# similar, but that doesn't work with the Cray compiler
# wrappers. Since Boost doesn't use the MPI C++ bindings,
# that can be used as a compiler option instead.
mpi_line = "using mpi : %s" % spec["mpi"].mpicxx
mpi_line = "using mpi : %s" % Path(spec["mpi"].mpicxx).as_posix()
f.write(mpi_line + " ;\n")
if spec.satisfies("+python"):
@ -608,6 +617,16 @@ def determine_b2_options(self, spec, options):
options.extend(["link=%s" % ",".join(link_types), "--layout=%s" % layout])
if spec.satisfies("platform=windows"):
# The runtime link must either be shared or static, not both.
if "+shared" in spec:
options.append("runtime-link=shared")
else:
options.append("runtime-link=static")
for lib in self.all_libs:
if f"+{lib}" not in spec:
options.append(f"--without-{lib}")
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
@ -671,6 +690,23 @@ def add_buildopt_symlinks(self, prefix):
prefix, remainder = lib.split(".", 1)
symlink(lib, "%s-mt.%s" % (prefix, remainder))
def bootstrap_windows(self):
"""Run the Windows-specific bootstrap.bat. The only bootstrapping command
line option that is accepted by the bootstrap.bat script is the compiler
information: either the vc version (e.g. MSVC 14.3.x would be vc143)
or gcc or clang.
"""
bootstrap_options = list()
if self.spec.satisfies("%msvc"):
bootstrap_options.append(f"vc{self.compiler.platform_toolset_ver}")
elif self.spec.satisfies("%gcc"):
bootstrap_options.append("gcc")
elif self.spec.satisfies("%clang"):
bootstrap_options.append("clang")
bootstrap = Executable("cmd.exe")
bootstrap("/c", ".\\bootstrap.bat", *bootstrap_options)
def install(self, spec, prefix):
# On Darwin, Boost expects the Darwin libtool. However, one of the
# dependencies may have pulled in Spack's GNU libtool, and these two
@ -710,16 +746,13 @@ def install(self, spec, prefix):
if spec.satisfies("+graph") and spec.satisfies("+mpi"):
with_libs.add("graph_parallel")
# to make Boost find the user-config.jam
env["BOOST_BUILD_PATH"] = self.stage.source_path
bootstrap_options = ["--prefix=%s" % prefix]
self.determine_bootstrap_options(spec, with_libs, bootstrap_options)
if self.spec.satisfies("platform=windows"):
bootstrap = Executable("cmd.exe")
bootstrap("/c", ".\\bootstrap.bat", *bootstrap_options)
self.bootstrap_windows()
else:
# to make Boost find the user-config.jam
env["BOOST_BUILD_PATH"] = self.stage.source_path
bootstrap_options = ["--prefix=%s" % prefix]
self.determine_bootstrap_options(spec, with_libs, bootstrap_options)
bootstrap = Executable("./bootstrap.sh")
bootstrap(*bootstrap_options)
@ -742,15 +775,24 @@ def install(self, spec, prefix):
if jobs > 64 and spec.satisfies("@:1.58"):
jobs = 64
# 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)
if self.spec.satisfies("platform=windows"):
def is_64bit():
# TODO: This method should be abstracted to a more general location
# as it is repeated in many places (msmpi.py for one)
return "64" in str(self.spec.target.family)
b2_options = [f"--prefix={self.prefix}", f"address-model={64 if is_64bit() else 32}"]
if not self.spec.satisfies("+python"):
b2_options.append("--without-python")
self.write_jam_file(self.spec)
else:
b2_options = ["-j", "%s" % jobs]
path_to_config = "--user-config=%s" % os.path.join(
self.stage.source_path, "user-config.jam"
)
b2_options.append(path_to_config)
threading_opts = self.determine_b2_options(spec, b2_options)
# Create headers if building from a git checkout

View File

@ -1023,8 +1023,13 @@ def find_library(self, library):
win_root_dir,
]
# The Python shipped with Xcode command line tools isn't in any of these locations
for subdir in ["lib", "lib64"]:
if self.spec.satisfies("platform=windows"):
lib_dirs = ["libs"]
else:
# The Python shipped with Xcode command line tools isn't in any of these locations
lib_dirs = ["lib", "lib64"]
for subdir in lib_dirs:
directories.append(os.path.join(self.config_vars["base"], subdir))
directories = dedupe(directories)
@ -1067,14 +1072,16 @@ def libs(self):
# The +shared variant isn't reliable, as `spack external find` currently can't
# detect it. If +shared, prefer the shared libraries, but check for static if
# those aren't found. Vice versa for ~shared.
if "+shared" in self.spec:
if self.spec.satisfies("platform=windows"):
# Since we are searching for link libraries, on Windows search only for
# ".Lib" extensions by default as those represent import libraries for implict links.
candidates = static_libs
elif self.spec.satisfies("+shared"):
candidates = shared_libs + static_libs
else:
candidates = static_libs + shared_libs
candidates = dedupe(candidates)
for candidate in candidates:
for candidate in dedupe(candidates):
lib = self.find_library(candidate)
if lib:
return lib