Improve type hints for package API (#47576)
by disentangling `package_base`, `builder` and `directives`.
This commit is contained in:
@@ -210,7 +210,7 @@ def setup(sphinx):
|
||||
# Spack classes that are private and we don't want to expose
|
||||
("py:class", "spack.provider_index._IndexBase"),
|
||||
("py:class", "spack.repo._PrependFileLoader"),
|
||||
("py:class", "spack.build_systems._checks.BaseBuilder"),
|
||||
("py:class", "spack.build_systems._checks.BuilderWithDefaults"),
|
||||
# Spack classes that intersphinx is unable to resolve
|
||||
("py:class", "spack.version.StandardVersion"),
|
||||
("py:class", "spack.spec.DependencySpec"),
|
||||
|
@@ -2925,9 +2925,9 @@ make sense during the build phase may not be needed at runtime, and vice versa.
|
||||
it makes sense to let a dependency set the environment variables for its dependents. To allow all
|
||||
this, Spack provides four different methods that can be overridden in a package:
|
||||
|
||||
1. :meth:`setup_build_environment <spack.builder.Builder.setup_build_environment>`
|
||||
1. :meth:`setup_build_environment <spack.builder.BaseBuilder.setup_build_environment>`
|
||||
2. :meth:`setup_run_environment <spack.package_base.PackageBase.setup_run_environment>`
|
||||
3. :meth:`setup_dependent_build_environment <spack.builder.Builder.setup_dependent_build_environment>`
|
||||
3. :meth:`setup_dependent_build_environment <spack.builder.BaseBuilder.setup_dependent_build_environment>`
|
||||
4. :meth:`setup_dependent_run_environment <spack.package_base.PackageBase.setup_dependent_run_environment>`
|
||||
|
||||
The Qt package, for instance, uses this call:
|
||||
|
@@ -56,7 +56,6 @@
|
||||
from llnl.util.symlink import symlink
|
||||
from llnl.util.tty.color import cescape, colorize
|
||||
|
||||
import spack.build_systems._checks
|
||||
import spack.build_systems.cmake
|
||||
import spack.build_systems.meson
|
||||
import spack.build_systems.python
|
||||
@@ -1375,7 +1374,7 @@ def exitcode_msg(p):
|
||||
return child_result
|
||||
|
||||
|
||||
CONTEXT_BASES = (spack.package_base.PackageBase, spack.build_systems._checks.BaseBuilder)
|
||||
CONTEXT_BASES = (spack.package_base.PackageBase, spack.builder.Builder)
|
||||
|
||||
|
||||
def get_package_context(traceback, context=3):
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
import spack.builder
|
||||
import spack.error
|
||||
import spack.phase_callbacks
|
||||
import spack.relocate
|
||||
import spack.spec
|
||||
import spack.store
|
||||
@@ -63,7 +64,7 @@ def apply_macos_rpath_fixups(builder: spack.builder.Builder):
|
||||
|
||||
|
||||
def ensure_build_dependencies_or_raise(
|
||||
spec: spack.spec.Spec, dependencies: List[spack.spec.Spec], error_msg: str
|
||||
spec: spack.spec.Spec, dependencies: List[str], error_msg: str
|
||||
):
|
||||
"""Ensure that some build dependencies are present in the concrete spec.
|
||||
|
||||
@@ -71,7 +72,7 @@ def ensure_build_dependencies_or_raise(
|
||||
|
||||
Args:
|
||||
spec: concrete spec to be checked.
|
||||
dependencies: list of abstract specs to be satisfied
|
||||
dependencies: list of package names of required build dependencies
|
||||
error_msg: brief error message to be prepended to a longer description
|
||||
|
||||
Raises:
|
||||
@@ -127,8 +128,8 @@ def execute_install_time_tests(builder: spack.builder.Builder):
|
||||
builder.pkg.tester.phase_tests(builder, "install", builder.install_time_test_callbacks)
|
||||
|
||||
|
||||
class BaseBuilder(spack.builder.Builder):
|
||||
"""Base class for builders to register common checks"""
|
||||
class BuilderWithDefaults(spack.builder.Builder):
|
||||
"""Base class for all specific builders with common callbacks registered."""
|
||||
|
||||
# Check that self.prefix is there after installation
|
||||
spack.builder.run_after("install")(sanity_check_prefix)
|
||||
spack.phase_callbacks.run_after("install")(sanity_check_prefix)
|
||||
|
@@ -6,7 +6,7 @@
|
||||
import os.path
|
||||
import stat
|
||||
import subprocess
|
||||
from typing import List
|
||||
from typing import Callable, List, Optional, Set, Tuple, Union
|
||||
|
||||
import llnl.util.filesystem as fs
|
||||
import llnl.util.tty as tty
|
||||
@@ -15,6 +15,9 @@
|
||||
import spack.builder
|
||||
import spack.error
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
import spack.spec
|
||||
import spack.util.prefix
|
||||
from spack.directives import build_system, conflicts, depends_on
|
||||
from spack.multimethod import when
|
||||
from spack.operating_systems.mac_os import macos_version
|
||||
@@ -22,7 +25,7 @@
|
||||
from spack.version import Version
|
||||
|
||||
from ._checks import (
|
||||
BaseBuilder,
|
||||
BuilderWithDefaults,
|
||||
apply_macos_rpath_fixups,
|
||||
ensure_build_dependencies_or_raise,
|
||||
execute_build_time_tests,
|
||||
@@ -69,14 +72,14 @@ def flags_to_build_system_args(self, flags):
|
||||
# Legacy methods (used by too many packages to change them,
|
||||
# need to forward to the builder)
|
||||
def enable_or_disable(self, *args, **kwargs):
|
||||
return self.builder.enable_or_disable(*args, **kwargs)
|
||||
return spack.builder.create(self).enable_or_disable(*args, **kwargs)
|
||||
|
||||
def with_or_without(self, *args, **kwargs):
|
||||
return self.builder.with_or_without(*args, **kwargs)
|
||||
return spack.builder.create(self).with_or_without(*args, **kwargs)
|
||||
|
||||
|
||||
@spack.builder.builder("autotools")
|
||||
class AutotoolsBuilder(BaseBuilder):
|
||||
class AutotoolsBuilder(BuilderWithDefaults):
|
||||
"""The autotools builder encodes the default way of installing software built
|
||||
with autotools. It has four phases that can be overridden, if need be:
|
||||
|
||||
@@ -157,7 +160,7 @@ class AutotoolsBuilder(BaseBuilder):
|
||||
install_libtool_archives = False
|
||||
|
||||
@property
|
||||
def patch_config_files(self):
|
||||
def patch_config_files(self) -> bool:
|
||||
"""Whether to update old ``config.guess`` and ``config.sub`` files
|
||||
distributed with the tarball.
|
||||
|
||||
@@ -177,7 +180,7 @@ def patch_config_files(self):
|
||||
)
|
||||
|
||||
@property
|
||||
def _removed_la_files_log(self):
|
||||
def _removed_la_files_log(self) -> str:
|
||||
"""File containing the list of removed libtool archives"""
|
||||
build_dir = self.build_directory
|
||||
if not os.path.isabs(self.build_directory):
|
||||
@@ -185,15 +188,15 @@ def _removed_la_files_log(self):
|
||||
return os.path.join(build_dir, "removed_la_files.txt")
|
||||
|
||||
@property
|
||||
def archive_files(self):
|
||||
def archive_files(self) -> List[str]:
|
||||
"""Files to archive for packages based on autotools"""
|
||||
files = [os.path.join(self.build_directory, "config.log")]
|
||||
if not self.install_libtool_archives:
|
||||
files.append(self._removed_la_files_log)
|
||||
return files
|
||||
|
||||
@spack.builder.run_after("autoreconf")
|
||||
def _do_patch_config_files(self):
|
||||
@spack.phase_callbacks.run_after("autoreconf")
|
||||
def _do_patch_config_files(self) -> None:
|
||||
"""Some packages ship with older config.guess/config.sub files and need to
|
||||
have these updated when installed on a newer architecture.
|
||||
|
||||
@@ -294,7 +297,7 @@ def runs_ok(script_abs_path):
|
||||
and set the prefix to the directory containing the `config.guess` and
|
||||
`config.sub` files.
|
||||
"""
|
||||
raise spack.error.InstallError(msg.format(", ".join(to_be_found), self.name))
|
||||
raise spack.error.InstallError(msg.format(", ".join(to_be_found), self.pkg.name))
|
||||
|
||||
# Copy the good files over the bad ones
|
||||
for abs_path in to_be_patched:
|
||||
@@ -304,8 +307,8 @@ def runs_ok(script_abs_path):
|
||||
fs.copy(substitutes[name], abs_path)
|
||||
os.chmod(abs_path, mode)
|
||||
|
||||
@spack.builder.run_before("configure")
|
||||
def _patch_usr_bin_file(self):
|
||||
@spack.phase_callbacks.run_before("configure")
|
||||
def _patch_usr_bin_file(self) -> None:
|
||||
"""On NixOS file is not available in /usr/bin/file. Patch configure
|
||||
scripts to use file from path."""
|
||||
|
||||
@@ -316,8 +319,8 @@ def _patch_usr_bin_file(self):
|
||||
with fs.keep_modification_time(*x.filenames):
|
||||
x.filter(regex="/usr/bin/file", repl="file", string=True)
|
||||
|
||||
@spack.builder.run_before("configure")
|
||||
def _set_autotools_environment_variables(self):
|
||||
@spack.phase_callbacks.run_before("configure")
|
||||
def _set_autotools_environment_variables(self) -> None:
|
||||
"""Many autotools builds use a version of mknod.m4 that fails when
|
||||
running as root unless FORCE_UNSAFE_CONFIGURE is set to 1.
|
||||
|
||||
@@ -330,8 +333,8 @@ def _set_autotools_environment_variables(self):
|
||||
"""
|
||||
os.environ["FORCE_UNSAFE_CONFIGURE"] = "1"
|
||||
|
||||
@spack.builder.run_before("configure")
|
||||
def _do_patch_libtool_configure(self):
|
||||
@spack.phase_callbacks.run_before("configure")
|
||||
def _do_patch_libtool_configure(self) -> None:
|
||||
"""Patch bugs that propagate from libtool macros into "configure" and
|
||||
further into "libtool". Note that patches that can be fixed by patching
|
||||
"libtool" directly should be implemented in the _do_patch_libtool method
|
||||
@@ -358,8 +361,8 @@ def _do_patch_libtool_configure(self):
|
||||
# Support Libtool 2.4.2 and older:
|
||||
x.filter(regex=r'^(\s*test \$p = "-R")(; then\s*)$', repl=r'\1 || test x-l = x"$p"\2')
|
||||
|
||||
@spack.builder.run_after("configure")
|
||||
def _do_patch_libtool(self):
|
||||
@spack.phase_callbacks.run_after("configure")
|
||||
def _do_patch_libtool(self) -> None:
|
||||
"""If configure generates a "libtool" script that does not correctly
|
||||
detect the compiler (and patch_libtool is set), patch in the correct
|
||||
values for libtool variables.
|
||||
@@ -507,27 +510,64 @@ def _do_patch_libtool(self):
|
||||
)
|
||||
|
||||
@property
|
||||
def configure_directory(self):
|
||||
def configure_directory(self) -> str:
|
||||
"""Return the directory where 'configure' resides."""
|
||||
return self.pkg.stage.source_path
|
||||
|
||||
@property
|
||||
def configure_abs_path(self):
|
||||
def configure_abs_path(self) -> str:
|
||||
# Absolute path to configure
|
||||
configure_abs_path = os.path.join(os.path.abspath(self.configure_directory), "configure")
|
||||
return configure_abs_path
|
||||
|
||||
@property
|
||||
def build_directory(self):
|
||||
def build_directory(self) -> str:
|
||||
"""Override to provide another place to build the package"""
|
||||
return self.configure_directory
|
||||
|
||||
@spack.builder.run_before("autoreconf")
|
||||
def delete_configure_to_force_update(self):
|
||||
@spack.phase_callbacks.run_before("autoreconf")
|
||||
def delete_configure_to_force_update(self) -> None:
|
||||
if self.force_autoreconf:
|
||||
fs.force_remove(self.configure_abs_path)
|
||||
|
||||
def autoreconf(self, pkg, spec, prefix):
|
||||
@property
|
||||
def autoreconf_search_path_args(self) -> List[str]:
|
||||
"""Search path includes for autoreconf. Add an -I flag for all `aclocal` dirs
|
||||
of build deps, skips the default path of automake, move external include
|
||||
flags to the back, since they might pull in unrelated m4 files shadowing
|
||||
spack dependencies."""
|
||||
return _autoreconf_search_path_args(self.spec)
|
||||
|
||||
@spack.phase_callbacks.run_after("autoreconf")
|
||||
def set_configure_or_die(self) -> None:
|
||||
"""Ensure the presence of a "configure" script, or raise. If the "configure"
|
||||
is found, a module level attribute is set.
|
||||
|
||||
Raises:
|
||||
RuntimeError: if the "configure" script is not found
|
||||
"""
|
||||
# Check if the "configure" script is there. If not raise a RuntimeError.
|
||||
if not os.path.exists(self.configure_abs_path):
|
||||
msg = "configure script not found in {0}"
|
||||
raise RuntimeError(msg.format(self.configure_directory))
|
||||
|
||||
# Monkey-patch the configure script in the corresponding module
|
||||
globals_for_pkg = spack.build_environment.ModuleChangePropagator(self.pkg)
|
||||
globals_for_pkg.configure = Executable(self.configure_abs_path)
|
||||
globals_for_pkg.propagate_changes_to_mro()
|
||||
|
||||
def configure_args(self) -> List[str]:
|
||||
"""Return the list of all the arguments that must be passed to configure,
|
||||
except ``--prefix`` which will be pre-pended to the list.
|
||||
"""
|
||||
return []
|
||||
|
||||
def autoreconf(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Not needed usually, configure should be already there"""
|
||||
|
||||
# If configure exists nothing needs to be done
|
||||
@@ -554,39 +594,12 @@ def autoreconf(self, pkg, spec, prefix):
|
||||
autoreconf_args += self.autoreconf_extra_args
|
||||
self.pkg.module.autoreconf(*autoreconf_args)
|
||||
|
||||
@property
|
||||
def autoreconf_search_path_args(self):
|
||||
"""Search path includes for autoreconf. Add an -I flag for all `aclocal` dirs
|
||||
of build deps, skips the default path of automake, move external include
|
||||
flags to the back, since they might pull in unrelated m4 files shadowing
|
||||
spack dependencies."""
|
||||
return _autoreconf_search_path_args(self.spec)
|
||||
|
||||
@spack.builder.run_after("autoreconf")
|
||||
def set_configure_or_die(self):
|
||||
"""Ensure the presence of a "configure" script, or raise. If the "configure"
|
||||
is found, a module level attribute is set.
|
||||
|
||||
Raises:
|
||||
RuntimeError: if the "configure" script is not found
|
||||
"""
|
||||
# Check if the "configure" script is there. If not raise a RuntimeError.
|
||||
if not os.path.exists(self.configure_abs_path):
|
||||
msg = "configure script not found in {0}"
|
||||
raise RuntimeError(msg.format(self.configure_directory))
|
||||
|
||||
# Monkey-patch the configure script in the corresponding module
|
||||
globals_for_pkg = spack.build_environment.ModuleChangePropagator(self.pkg)
|
||||
globals_for_pkg.configure = Executable(self.configure_abs_path)
|
||||
globals_for_pkg.propagate_changes_to_mro()
|
||||
|
||||
def configure_args(self):
|
||||
"""Return the list of all the arguments that must be passed to configure,
|
||||
except ``--prefix`` which will be pre-pended to the list.
|
||||
"""
|
||||
return []
|
||||
|
||||
def configure(self, pkg, spec, prefix):
|
||||
def configure(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Run "configure", with the arguments specified by the builder and an
|
||||
appropriately set prefix.
|
||||
"""
|
||||
@@ -597,7 +610,12 @@ def configure(self, pkg, spec, prefix):
|
||||
with fs.working_dir(self.build_directory, create=True):
|
||||
pkg.module.configure(*options)
|
||||
|
||||
def build(self, pkg, spec, prefix):
|
||||
def build(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Run "make" on the build targets specified by the builder."""
|
||||
# See https://autotools.io/automake/silent.html
|
||||
params = ["V=1"]
|
||||
@@ -605,41 +623,49 @@ def build(self, pkg, spec, prefix):
|
||||
with fs.working_dir(self.build_directory):
|
||||
pkg.module.make(*params)
|
||||
|
||||
def install(self, pkg, spec, prefix):
|
||||
def install(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Run "make" on the install targets specified by the builder."""
|
||||
with fs.working_dir(self.build_directory):
|
||||
pkg.module.make(*self.install_targets)
|
||||
|
||||
spack.builder.run_after("build")(execute_build_time_tests)
|
||||
spack.phase_callbacks.run_after("build")(execute_build_time_tests)
|
||||
|
||||
def check(self):
|
||||
def check(self) -> None:
|
||||
"""Run "make" on the ``test`` and ``check`` targets, if found."""
|
||||
with fs.working_dir(self.build_directory):
|
||||
self.pkg._if_make_target_execute("test")
|
||||
self.pkg._if_make_target_execute("check")
|
||||
|
||||
def _activate_or_not(
|
||||
self, name, activation_word, deactivation_word, activation_value=None, variant=None
|
||||
):
|
||||
self,
|
||||
name: str,
|
||||
activation_word: str,
|
||||
deactivation_word: str,
|
||||
activation_value: Optional[Union[Callable, str]] = None,
|
||||
variant=None,
|
||||
) -> List[str]:
|
||||
"""This function contain the current implementation details of
|
||||
:meth:`~spack.build_systems.autotools.AutotoolsBuilder.with_or_without` and
|
||||
:meth:`~spack.build_systems.autotools.AutotoolsBuilder.enable_or_disable`.
|
||||
|
||||
Args:
|
||||
name (str): name of the option that is being activated or not
|
||||
activation_word (str): the default activation word ('with' in the
|
||||
case of ``with_or_without``)
|
||||
deactivation_word (str): the default deactivation word ('without'
|
||||
in the case of ``with_or_without``)
|
||||
activation_value (typing.Callable): callable that accepts a single
|
||||
value. This value is either one of the allowed values for a
|
||||
multi-valued variant or the name of a bool-valued variant.
|
||||
name: name of the option that is being activated or not
|
||||
activation_word: the default activation word ('with' in the case of
|
||||
``with_or_without``)
|
||||
deactivation_word: the default deactivation word ('without' in the case of
|
||||
``with_or_without``)
|
||||
activation_value: callable that accepts a single value. This value is either one of the
|
||||
allowed values for a multi-valued variant or the name of a bool-valued variant.
|
||||
Returns the parameter to be used when the value is activated.
|
||||
|
||||
The special value 'prefix' can also be assigned and will return
|
||||
The special value "prefix" can also be assigned and will return
|
||||
``spec[name].prefix`` as activation parameter.
|
||||
variant (str): name of the variant that is being processed
|
||||
(if different from option name)
|
||||
variant: name of the variant that is being processed (if different from option name)
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -647,19 +673,19 @@ def _activate_or_not(
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
variant('foo', values=('x', 'y'), description='')
|
||||
variant('bar', default=True, description='')
|
||||
variant('ba_z', default=True, description='')
|
||||
variant("foo", values=("x", "y"), description=")
|
||||
variant("bar", default=True, description=")
|
||||
variant("ba_z", default=True, description=")
|
||||
|
||||
calling this function like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
_activate_or_not(
|
||||
'foo', 'with', 'without', activation_value='prefix'
|
||||
"foo", "with", "without", activation_value="prefix"
|
||||
)
|
||||
_activate_or_not('bar', 'with', 'without')
|
||||
_activate_or_not('ba-z', 'with', 'without', variant='ba_z')
|
||||
_activate_or_not("bar", "with", "without")
|
||||
_activate_or_not("ba-z", "with", "without", variant="ba_z")
|
||||
|
||||
will generate the following configuration options:
|
||||
|
||||
@@ -679,8 +705,8 @@ def _activate_or_not(
|
||||
Raises:
|
||||
KeyError: if name is not among known variants
|
||||
"""
|
||||
spec = self.pkg.spec
|
||||
args = []
|
||||
spec: spack.spec.Spec = self.pkg.spec
|
||||
args: List[str] = []
|
||||
|
||||
if activation_value == "prefix":
|
||||
activation_value = lambda x: spec[x].prefix
|
||||
@@ -698,7 +724,7 @@ def _activate_or_not(
|
||||
# Create a list of pairs. Each pair includes a configuration
|
||||
# option and whether or not that option is activated
|
||||
vdef = self.pkg.get_variant(variant)
|
||||
if set(vdef.values) == set((True, False)):
|
||||
if set(vdef.values) == set((True, False)): # type: ignore
|
||||
# BoolValuedVariant carry information about a single option.
|
||||
# Nonetheless, for uniformity of treatment we'll package them
|
||||
# in an iterable of one element.
|
||||
@@ -709,14 +735,12 @@ def _activate_or_not(
|
||||
# package's build system. It excludes values which have special
|
||||
# meanings and do not correspond to features (e.g. "none")
|
||||
feature_values = getattr(vdef.values, "feature_values", None) or vdef.values
|
||||
options = [(value, f"{variant}={value}" in spec) for value in feature_values]
|
||||
options = [(v, f"{variant}={v}" in spec) for v in feature_values] # type: ignore
|
||||
|
||||
# For each allowed value in the list of values
|
||||
for option_value, activated in options:
|
||||
# Search for an override in the package for this value
|
||||
override_name = "{0}_or_{1}_{2}".format(
|
||||
activation_word, deactivation_word, option_value
|
||||
)
|
||||
override_name = f"{activation_word}_or_{deactivation_word}_{option_value}"
|
||||
line_generator = getattr(self, override_name, None) or getattr(
|
||||
self.pkg, override_name, None
|
||||
)
|
||||
@@ -725,19 +749,24 @@ def _activate_or_not(
|
||||
|
||||
def _default_generator(is_activated):
|
||||
if is_activated:
|
||||
line = "--{0}-{1}".format(activation_word, option_value)
|
||||
line = f"--{activation_word}-{option_value}"
|
||||
if activation_value is not None and activation_value(
|
||||
option_value
|
||||
): # NOQA=ignore=E501
|
||||
line += "={0}".format(activation_value(option_value))
|
||||
line = f"{line}={activation_value(option_value)}"
|
||||
return line
|
||||
return "--{0}-{1}".format(deactivation_word, option_value)
|
||||
return f"--{deactivation_word}-{option_value}"
|
||||
|
||||
line_generator = _default_generator
|
||||
args.append(line_generator(activated))
|
||||
return args
|
||||
|
||||
def with_or_without(self, name, activation_value=None, variant=None):
|
||||
def with_or_without(
|
||||
self,
|
||||
name: str,
|
||||
activation_value: Optional[Union[Callable, str]] = None,
|
||||
variant: Optional[str] = None,
|
||||
) -> List[str]:
|
||||
"""Inspects a variant and returns the arguments that activate
|
||||
or deactivate the selected feature(s) for the configure options.
|
||||
|
||||
@@ -752,12 +781,11 @@ def with_or_without(self, name, activation_value=None, variant=None):
|
||||
``variant=value`` is in the spec.
|
||||
|
||||
Args:
|
||||
name (str): name of a valid multi-valued variant
|
||||
activation_value (typing.Callable): callable that accepts a single
|
||||
value and returns the parameter to be used leading to an entry
|
||||
of the type ``--with-{name}={parameter}``.
|
||||
name: name of a valid multi-valued variant
|
||||
activation_value: callable that accepts a single value and returns the parameter to be
|
||||
used leading to an entry of the type ``--with-{name}={parameter}``.
|
||||
|
||||
The special value 'prefix' can also be assigned and will return
|
||||
The special value "prefix" can also be assigned and will return
|
||||
``spec[name].prefix`` as activation parameter.
|
||||
|
||||
Returns:
|
||||
@@ -765,18 +793,22 @@ def with_or_without(self, name, activation_value=None, variant=None):
|
||||
"""
|
||||
return self._activate_or_not(name, "with", "without", activation_value, variant)
|
||||
|
||||
def enable_or_disable(self, name, activation_value=None, variant=None):
|
||||
def enable_or_disable(
|
||||
self,
|
||||
name: str,
|
||||
activation_value: Optional[Union[Callable, str]] = None,
|
||||
variant: Optional[str] = None,
|
||||
) -> List[str]:
|
||||
"""Same as
|
||||
:meth:`~spack.build_systems.autotools.AutotoolsBuilder.with_or_without`
|
||||
but substitute ``with`` with ``enable`` and ``without`` with ``disable``.
|
||||
|
||||
Args:
|
||||
name (str): name of a valid multi-valued variant
|
||||
activation_value (typing.Callable): if present accepts a single value
|
||||
and returns the parameter to be used leading to an entry of the
|
||||
type ``--enable-{name}={parameter}``
|
||||
name: name of a valid multi-valued variant
|
||||
activation_value: if present accepts a single value and returns the parameter to be
|
||||
used leading to an entry of the type ``--enable-{name}={parameter}``
|
||||
|
||||
The special value 'prefix' can also be assigned and will return
|
||||
The special value "prefix" can also be assigned and will return
|
||||
``spec[name].prefix`` as activation parameter.
|
||||
|
||||
Returns:
|
||||
@@ -784,15 +816,15 @@ def enable_or_disable(self, name, activation_value=None, variant=None):
|
||||
"""
|
||||
return self._activate_or_not(name, "enable", "disable", activation_value, variant)
|
||||
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
||||
def installcheck(self):
|
||||
def installcheck(self) -> None:
|
||||
"""Run "make" on the ``installcheck`` target, if found."""
|
||||
with fs.working_dir(self.build_directory):
|
||||
self.pkg._if_make_target_execute("installcheck")
|
||||
|
||||
@spack.builder.run_after("install")
|
||||
def remove_libtool_archives(self):
|
||||
@spack.phase_callbacks.run_after("install")
|
||||
def remove_libtool_archives(self) -> None:
|
||||
"""Remove all .la files in prefix sub-folders if the package sets
|
||||
``install_libtool_archives`` to be False.
|
||||
"""
|
||||
@@ -814,12 +846,13 @@ def setup_build_environment(self, env):
|
||||
env.set("MACOSX_DEPLOYMENT_TARGET", "10.16")
|
||||
|
||||
# On macOS, force rpaths for shared library IDs and remove duplicate rpaths
|
||||
spack.builder.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
|
||||
spack.phase_callbacks.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
|
||||
|
||||
|
||||
def _autoreconf_search_path_args(spec):
|
||||
dirs_seen = set()
|
||||
flags_spack, flags_external = [], []
|
||||
def _autoreconf_search_path_args(spec: spack.spec.Spec) -> List[str]:
|
||||
dirs_seen: Set[Tuple[int, int]] = set()
|
||||
flags_spack: List[str] = []
|
||||
flags_external: List[str] = []
|
||||
|
||||
# We don't want to add an include flag for automake's default search path.
|
||||
for automake in spec.dependencies(name="automake", deptype="build"):
|
||||
|
@@ -10,7 +10,7 @@
|
||||
import llnl.util.filesystem as fs
|
||||
import llnl.util.tty as tty
|
||||
|
||||
import spack.builder
|
||||
import spack.phase_callbacks
|
||||
|
||||
from .cmake import CMakeBuilder, CMakePackage
|
||||
|
||||
@@ -332,7 +332,7 @@ def std_cmake_args(self):
|
||||
args.extend(["-C", self.cache_path])
|
||||
return args
|
||||
|
||||
@spack.builder.run_after("install")
|
||||
@spack.phase_callbacks.run_after("install")
|
||||
def install_cmake_cache(self):
|
||||
fs.mkdirp(self.pkg.spec.prefix.share.cmake)
|
||||
fs.install(self.cache_path, self.pkg.spec.prefix.share.cmake)
|
||||
|
@@ -7,10 +7,11 @@
|
||||
|
||||
import spack.builder
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
from spack.directives import build_system, depends_on
|
||||
from spack.multimethod import when
|
||||
|
||||
from ._checks import BaseBuilder, execute_install_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_install_time_tests
|
||||
|
||||
|
||||
class CargoPackage(spack.package_base.PackageBase):
|
||||
@@ -27,7 +28,7 @@ class CargoPackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("cargo")
|
||||
class CargoBuilder(BaseBuilder):
|
||||
class CargoBuilder(BuilderWithDefaults):
|
||||
"""The Cargo builder encodes the most common way of building software with
|
||||
a rust Cargo.toml file. It has two phases that can be overridden, if need be:
|
||||
|
||||
@@ -77,7 +78,7 @@ def install(self, pkg, spec, prefix):
|
||||
with fs.working_dir(self.build_directory):
|
||||
fs.install_tree("out", prefix)
|
||||
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
||||
def check(self):
|
||||
"""Run "cargo test"."""
|
||||
|
@@ -9,7 +9,7 @@
|
||||
import re
|
||||
import sys
|
||||
from itertools import chain
|
||||
from typing import List, Optional, Set, Tuple
|
||||
from typing import Any, List, Optional, Set, Tuple
|
||||
|
||||
import llnl.util.filesystem as fs
|
||||
from llnl.util.lang import stable_partition
|
||||
@@ -18,11 +18,14 @@
|
||||
import spack.deptypes as dt
|
||||
import spack.error
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
import spack.spec
|
||||
import spack.util.prefix
|
||||
from spack.directives import build_system, conflicts, depends_on, variant
|
||||
from spack.multimethod import when
|
||||
from spack.util.environment import filter_system_paths
|
||||
|
||||
from ._checks import BaseBuilder, execute_build_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
||||
|
||||
# Regex to extract the primary generator from the CMake generator
|
||||
# string.
|
||||
@@ -48,9 +51,9 @@ def _maybe_set_python_hints(pkg: spack.package_base.PackageBase, args: List[str]
|
||||
python_executable = pkg.spec["python"].command.path
|
||||
args.extend(
|
||||
[
|
||||
CMakeBuilder.define("PYTHON_EXECUTABLE", python_executable),
|
||||
CMakeBuilder.define("Python_EXECUTABLE", python_executable),
|
||||
CMakeBuilder.define("Python3_EXECUTABLE", python_executable),
|
||||
define("PYTHON_EXECUTABLE", python_executable),
|
||||
define("Python_EXECUTABLE", python_executable),
|
||||
define("Python3_EXECUTABLE", python_executable),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -85,7 +88,7 @@ def _conditional_cmake_defaults(pkg: spack.package_base.PackageBase, args: List[
|
||||
ipo = False
|
||||
|
||||
if cmake.satisfies("@3.9:"):
|
||||
args.append(CMakeBuilder.define("CMAKE_INTERPROCEDURAL_OPTIMIZATION", ipo))
|
||||
args.append(define("CMAKE_INTERPROCEDURAL_OPTIMIZATION", ipo))
|
||||
|
||||
# Disable Package Registry: export(PACKAGE) may put files in the user's home directory, and
|
||||
# find_package may search there. This is not what we want.
|
||||
@@ -93,30 +96,36 @@ def _conditional_cmake_defaults(pkg: spack.package_base.PackageBase, args: List[
|
||||
# Do not populate CMake User Package Registry
|
||||
if cmake.satisfies("@3.15:"):
|
||||
# see https://cmake.org/cmake/help/latest/policy/CMP0090.html
|
||||
args.append(CMakeBuilder.define("CMAKE_POLICY_DEFAULT_CMP0090", "NEW"))
|
||||
args.append(define("CMAKE_POLICY_DEFAULT_CMP0090", "NEW"))
|
||||
elif cmake.satisfies("@3.1:"):
|
||||
# see https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY.html
|
||||
args.append(CMakeBuilder.define("CMAKE_EXPORT_NO_PACKAGE_REGISTRY", True))
|
||||
args.append(define("CMAKE_EXPORT_NO_PACKAGE_REGISTRY", True))
|
||||
|
||||
# Do not use CMake User/System Package Registry
|
||||
# https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#disabling-the-package-registry
|
||||
if cmake.satisfies("@3.16:"):
|
||||
args.append(CMakeBuilder.define("CMAKE_FIND_USE_PACKAGE_REGISTRY", False))
|
||||
args.append(define("CMAKE_FIND_USE_PACKAGE_REGISTRY", False))
|
||||
elif cmake.satisfies("@3.1:3.15"):
|
||||
args.append(CMakeBuilder.define("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY", False))
|
||||
args.append(CMakeBuilder.define("CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY", False))
|
||||
args.append(define("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY", False))
|
||||
args.append(define("CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY", False))
|
||||
|
||||
# Export a compilation database if supported.
|
||||
if _supports_compilation_databases(pkg):
|
||||
args.append(CMakeBuilder.define("CMAKE_EXPORT_COMPILE_COMMANDS", True))
|
||||
args.append(define("CMAKE_EXPORT_COMPILE_COMMANDS", True))
|
||||
|
||||
# Enable MACOSX_RPATH by default when cmake_minimum_required < 3
|
||||
# https://cmake.org/cmake/help/latest/policy/CMP0042.html
|
||||
if pkg.spec.satisfies("platform=darwin") and cmake.satisfies("@3:"):
|
||||
args.append(CMakeBuilder.define("CMAKE_POLICY_DEFAULT_CMP0042", "NEW"))
|
||||
args.append(define("CMAKE_POLICY_DEFAULT_CMP0042", "NEW"))
|
||||
|
||||
# Disable find package's config mode for versions of Boost that
|
||||
# didn't provide it. See https://github.com/spack/spack/issues/20169
|
||||
# and https://cmake.org/cmake/help/latest/module/FindBoost.html
|
||||
if pkg.spec.satisfies("^boost@:1.69.0"):
|
||||
args.append(define("Boost_NO_BOOST_CMAKE", True))
|
||||
|
||||
|
||||
def generator(*names: str, default: Optional[str] = None):
|
||||
def generator(*names: str, default: Optional[str] = None) -> None:
|
||||
"""The build system generator to use.
|
||||
|
||||
See ``cmake --help`` for a list of valid generators.
|
||||
@@ -263,15 +272,15 @@ def flags_to_build_system_args(self, flags):
|
||||
|
||||
# Legacy methods (used by too many packages to change them,
|
||||
# need to forward to the builder)
|
||||
def define(self, *args, **kwargs):
|
||||
return self.builder.define(*args, **kwargs)
|
||||
def define(self, cmake_var: str, value: Any) -> str:
|
||||
return define(cmake_var, value)
|
||||
|
||||
def define_from_variant(self, *args, **kwargs):
|
||||
return self.builder.define_from_variant(*args, **kwargs)
|
||||
def define_from_variant(self, cmake_var: str, variant: Optional[str] = None) -> str:
|
||||
return define_from_variant(self, cmake_var, variant)
|
||||
|
||||
|
||||
@spack.builder.builder("cmake")
|
||||
class CMakeBuilder(BaseBuilder):
|
||||
class CMakeBuilder(BuilderWithDefaults):
|
||||
"""The cmake builder encodes the default way of building software with CMake. IT
|
||||
has three phases that can be overridden:
|
||||
|
||||
@@ -321,15 +330,15 @@ class CMakeBuilder(BaseBuilder):
|
||||
build_time_test_callbacks = ["check"]
|
||||
|
||||
@property
|
||||
def archive_files(self):
|
||||
def archive_files(self) -> List[str]:
|
||||
"""Files to archive for packages based on CMake"""
|
||||
files = [os.path.join(self.build_directory, "CMakeCache.txt")]
|
||||
if _supports_compilation_databases(self):
|
||||
if _supports_compilation_databases(self.pkg):
|
||||
files.append(os.path.join(self.build_directory, "compile_commands.json"))
|
||||
return files
|
||||
|
||||
@property
|
||||
def root_cmakelists_dir(self):
|
||||
def root_cmakelists_dir(self) -> str:
|
||||
"""The relative path to the directory containing CMakeLists.txt
|
||||
|
||||
This path is relative to the root of the extracted tarball,
|
||||
@@ -338,16 +347,17 @@ def root_cmakelists_dir(self):
|
||||
return self.pkg.stage.source_path
|
||||
|
||||
@property
|
||||
def generator(self):
|
||||
def generator(self) -> str:
|
||||
if self.spec.satisfies("generator=make"):
|
||||
return "Unix Makefiles"
|
||||
if self.spec.satisfies("generator=ninja"):
|
||||
return "Ninja"
|
||||
msg = f'{self.spec.format()} has an unsupported value for the "generator" variant'
|
||||
raise ValueError(msg)
|
||||
raise ValueError(
|
||||
f'{self.spec.format()} has an unsupported value for the "generator" variant'
|
||||
)
|
||||
|
||||
@property
|
||||
def std_cmake_args(self):
|
||||
def std_cmake_args(self) -> List[str]:
|
||||
"""Standard cmake arguments provided as a property for
|
||||
convenience of package writers
|
||||
"""
|
||||
@@ -356,7 +366,9 @@ def std_cmake_args(self):
|
||||
return args
|
||||
|
||||
@staticmethod
|
||||
def std_args(pkg, generator=None):
|
||||
def std_args(
|
||||
pkg: spack.package_base.PackageBase, generator: Optional[str] = None
|
||||
) -> List[str]:
|
||||
"""Computes the standard cmake arguments for a generic package"""
|
||||
default_generator = "Ninja" if sys.platform == "win32" else "Unix Makefiles"
|
||||
generator = generator or default_generator
|
||||
@@ -373,7 +385,6 @@ def std_args(pkg, generator=None):
|
||||
except KeyError:
|
||||
build_type = "RelWithDebInfo"
|
||||
|
||||
define = CMakeBuilder.define
|
||||
args = [
|
||||
"-G",
|
||||
generator,
|
||||
@@ -405,152 +416,31 @@ def std_args(pkg, generator=None):
|
||||
return args
|
||||
|
||||
@staticmethod
|
||||
def define_cuda_architectures(pkg):
|
||||
"""Returns the str ``-DCMAKE_CUDA_ARCHITECTURES:STRING=(expanded cuda_arch)``.
|
||||
|
||||
``cuda_arch`` is variant composed of a list of target CUDA architectures and
|
||||
it is declared in the cuda package.
|
||||
|
||||
This method is no-op for cmake<3.18 and when ``cuda_arch`` variant is not set.
|
||||
|
||||
"""
|
||||
cmake_flag = str()
|
||||
if "cuda_arch" in pkg.spec.variants and pkg.spec.satisfies("^cmake@3.18:"):
|
||||
cmake_flag = CMakeBuilder.define(
|
||||
"CMAKE_CUDA_ARCHITECTURES", pkg.spec.variants["cuda_arch"].value
|
||||
)
|
||||
|
||||
return cmake_flag
|
||||
def define_cuda_architectures(pkg: spack.package_base.PackageBase) -> str:
|
||||
return define_cuda_architectures(pkg)
|
||||
|
||||
@staticmethod
|
||||
def define_hip_architectures(pkg):
|
||||
"""Returns the str ``-DCMAKE_HIP_ARCHITECTURES:STRING=(expanded amdgpu_target)``.
|
||||
|
||||
``amdgpu_target`` is variant composed of a list of the target HIP
|
||||
architectures and it is declared in the rocm package.
|
||||
|
||||
This method is no-op for cmake<3.18 and when ``amdgpu_target`` variant is
|
||||
not set.
|
||||
|
||||
"""
|
||||
cmake_flag = str()
|
||||
if "amdgpu_target" in pkg.spec.variants and pkg.spec.satisfies("^cmake@3.21:"):
|
||||
cmake_flag = CMakeBuilder.define(
|
||||
"CMAKE_HIP_ARCHITECTURES", pkg.spec.variants["amdgpu_target"].value
|
||||
)
|
||||
|
||||
return cmake_flag
|
||||
def define_hip_architectures(pkg: spack.package_base.PackageBase) -> str:
|
||||
return define_hip_architectures(pkg)
|
||||
|
||||
@staticmethod
|
||||
def define(cmake_var, value):
|
||||
"""Return a CMake command line argument that defines a variable.
|
||||
def define(cmake_var: str, value: Any) -> str:
|
||||
return define(cmake_var, value)
|
||||
|
||||
The resulting argument will convert boolean values to OFF/ON
|
||||
and lists/tuples to CMake semicolon-separated string lists. All other
|
||||
values will be interpreted as strings.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[define('BUILD_SHARED_LIBS', True),
|
||||
define('CMAKE_CXX_STANDARD', 14),
|
||||
define('swr', ['avx', 'avx2'])]
|
||||
|
||||
will generate the following configuration options:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
["-DBUILD_SHARED_LIBS:BOOL=ON",
|
||||
"-DCMAKE_CXX_STANDARD:STRING=14",
|
||||
"-DSWR:STRING=avx;avx2]
|
||||
|
||||
"""
|
||||
# Create a list of pairs. Each pair includes a configuration
|
||||
# option and whether or not that option is activated
|
||||
if isinstance(value, bool):
|
||||
kind = "BOOL"
|
||||
value = "ON" if value else "OFF"
|
||||
else:
|
||||
kind = "STRING"
|
||||
if isinstance(value, collections.abc.Sequence) and not isinstance(value, str):
|
||||
value = ";".join(str(v) for v in value)
|
||||
else:
|
||||
value = str(value)
|
||||
|
||||
return "".join(["-D", cmake_var, ":", kind, "=", value])
|
||||
|
||||
def define_from_variant(self, cmake_var, variant=None):
|
||||
"""Return a CMake command line argument from the given variant's value.
|
||||
|
||||
The optional ``variant`` argument defaults to the lower-case transform
|
||||
of ``cmake_var``.
|
||||
|
||||
This utility function is similar to
|
||||
:meth:`~spack.build_systems.autotools.AutotoolsBuilder.with_or_without`.
|
||||
|
||||
Examples:
|
||||
|
||||
Given a package with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
variant('cxxstd', default='11', values=('11', '14'),
|
||||
multi=False, description='')
|
||||
variant('shared', default=True, description='')
|
||||
variant('swr', values=any_combination_of('avx', 'avx2'),
|
||||
description='')
|
||||
|
||||
calling this function like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[self.define_from_variant('BUILD_SHARED_LIBS', 'shared'),
|
||||
self.define_from_variant('CMAKE_CXX_STANDARD', 'cxxstd'),
|
||||
self.define_from_variant('SWR')]
|
||||
|
||||
will generate the following configuration options:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
["-DBUILD_SHARED_LIBS:BOOL=ON",
|
||||
"-DCMAKE_CXX_STANDARD:STRING=14",
|
||||
"-DSWR:STRING=avx;avx2]
|
||||
|
||||
for ``<spec-name> cxxstd=14 +shared swr=avx,avx2``
|
||||
|
||||
Note: if the provided variant is conditional, and the condition is not met,
|
||||
this function returns an empty string. CMake discards empty strings
|
||||
provided on the command line.
|
||||
"""
|
||||
|
||||
if variant is None:
|
||||
variant = cmake_var.lower()
|
||||
|
||||
if not self.pkg.has_variant(variant):
|
||||
raise KeyError('"{0}" is not a variant of "{1}"'.format(variant, self.pkg.name))
|
||||
|
||||
if variant not in self.pkg.spec.variants:
|
||||
return ""
|
||||
|
||||
value = self.pkg.spec.variants[variant].value
|
||||
if isinstance(value, (tuple, list)):
|
||||
# Sort multi-valued variants for reproducibility
|
||||
value = sorted(value)
|
||||
|
||||
return self.define(cmake_var, value)
|
||||
def define_from_variant(self, cmake_var: str, variant: Optional[str] = None) -> str:
|
||||
return define_from_variant(self.pkg, cmake_var, variant)
|
||||
|
||||
@property
|
||||
def build_dirname(self):
|
||||
def build_dirname(self) -> str:
|
||||
"""Directory name to use when building the package."""
|
||||
return "spack-build-%s" % self.pkg.spec.dag_hash(7)
|
||||
return f"spack-build-{self.pkg.spec.dag_hash(7)}"
|
||||
|
||||
@property
|
||||
def build_directory(self):
|
||||
def build_directory(self) -> str:
|
||||
"""Full-path to the directory to use when building the package."""
|
||||
return os.path.join(self.pkg.stage.path, self.build_dirname)
|
||||
|
||||
def cmake_args(self):
|
||||
def cmake_args(self) -> List[str]:
|
||||
"""List of all the arguments that must be passed to cmake, except:
|
||||
|
||||
* CMAKE_INSTALL_PREFIX
|
||||
@@ -560,7 +450,12 @@ def cmake_args(self):
|
||||
"""
|
||||
return []
|
||||
|
||||
def cmake(self, pkg, spec, prefix):
|
||||
def cmake(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Runs ``cmake`` in the build directory"""
|
||||
|
||||
# skip cmake phase if it is an incremental develop build
|
||||
@@ -575,7 +470,12 @@ def cmake(self, pkg, spec, prefix):
|
||||
with fs.working_dir(self.build_directory, create=True):
|
||||
pkg.module.cmake(*options)
|
||||
|
||||
def build(self, pkg, spec, prefix):
|
||||
def build(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Make the build targets"""
|
||||
with fs.working_dir(self.build_directory):
|
||||
if self.generator == "Unix Makefiles":
|
||||
@@ -584,7 +484,12 @@ def build(self, pkg, spec, prefix):
|
||||
self.build_targets.append("-v")
|
||||
pkg.module.ninja(*self.build_targets)
|
||||
|
||||
def install(self, pkg, spec, prefix):
|
||||
def install(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Make the install targets"""
|
||||
with fs.working_dir(self.build_directory):
|
||||
if self.generator == "Unix Makefiles":
|
||||
@@ -592,9 +497,9 @@ def install(self, pkg, spec, prefix):
|
||||
elif self.generator == "Ninja":
|
||||
pkg.module.ninja(*self.install_targets)
|
||||
|
||||
spack.builder.run_after("build")(execute_build_time_tests)
|
||||
spack.phase_callbacks.run_after("build")(execute_build_time_tests)
|
||||
|
||||
def check(self):
|
||||
def check(self) -> None:
|
||||
"""Search the CMake-generated files for the targets ``test`` and ``check``,
|
||||
and runs them if found.
|
||||
"""
|
||||
@@ -605,3 +510,133 @@ def check(self):
|
||||
elif self.generator == "Ninja":
|
||||
self.pkg._if_ninja_target_execute("test", jobs_env="CTEST_PARALLEL_LEVEL")
|
||||
self.pkg._if_ninja_target_execute("check")
|
||||
|
||||
|
||||
def define(cmake_var: str, value: Any) -> str:
|
||||
"""Return a CMake command line argument that defines a variable.
|
||||
|
||||
The resulting argument will convert boolean values to OFF/ON and lists/tuples to CMake
|
||||
semicolon-separated string lists. All other values will be interpreted as strings.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[define("BUILD_SHARED_LIBS", True),
|
||||
define("CMAKE_CXX_STANDARD", 14),
|
||||
define("swr", ["avx", "avx2"])]
|
||||
|
||||
will generate the following configuration options:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
["-DBUILD_SHARED_LIBS:BOOL=ON",
|
||||
"-DCMAKE_CXX_STANDARD:STRING=14",
|
||||
"-DSWR:STRING=avx;avx2]
|
||||
|
||||
"""
|
||||
# Create a list of pairs. Each pair includes a configuration
|
||||
# option and whether or not that option is activated
|
||||
if isinstance(value, bool):
|
||||
kind = "BOOL"
|
||||
value = "ON" if value else "OFF"
|
||||
else:
|
||||
kind = "STRING"
|
||||
if isinstance(value, collections.abc.Sequence) and not isinstance(value, str):
|
||||
value = ";".join(str(v) for v in value)
|
||||
else:
|
||||
value = str(value)
|
||||
|
||||
return "".join(["-D", cmake_var, ":", kind, "=", value])
|
||||
|
||||
|
||||
def define_from_variant(
|
||||
pkg: spack.package_base.PackageBase, cmake_var: str, variant: Optional[str] = None
|
||||
) -> str:
|
||||
"""Return a CMake command line argument from the given variant's value.
|
||||
|
||||
The optional ``variant`` argument defaults to the lower-case transform
|
||||
of ``cmake_var``.
|
||||
|
||||
Examples:
|
||||
|
||||
Given a package with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
variant("cxxstd", default="11", values=("11", "14"),
|
||||
multi=False, description="")
|
||||
variant("shared", default=True, description="")
|
||||
variant("swr", values=any_combination_of("avx", "avx2"),
|
||||
description="")
|
||||
|
||||
calling this function like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[
|
||||
self.define_from_variant("BUILD_SHARED_LIBS", "shared"),
|
||||
self.define_from_variant("CMAKE_CXX_STANDARD", "cxxstd"),
|
||||
self.define_from_variant("SWR"),
|
||||
]
|
||||
|
||||
will generate the following configuration options:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
[
|
||||
"-DBUILD_SHARED_LIBS:BOOL=ON",
|
||||
"-DCMAKE_CXX_STANDARD:STRING=14",
|
||||
"-DSWR:STRING=avx;avx2",
|
||||
]
|
||||
|
||||
for ``<spec-name> cxxstd=14 +shared swr=avx,avx2``
|
||||
|
||||
Note: if the provided variant is conditional, and the condition is not met, this function
|
||||
returns an empty string. CMake discards empty strings provided on the command line.
|
||||
"""
|
||||
if variant is None:
|
||||
variant = cmake_var.lower()
|
||||
|
||||
if not pkg.has_variant(variant):
|
||||
raise KeyError('"{0}" is not a variant of "{1}"'.format(variant, pkg.name))
|
||||
|
||||
if variant not in pkg.spec.variants:
|
||||
return ""
|
||||
|
||||
value = pkg.spec.variants[variant].value
|
||||
if isinstance(value, (tuple, list)):
|
||||
# Sort multi-valued variants for reproducibility
|
||||
value = sorted(value)
|
||||
|
||||
return define(cmake_var, value)
|
||||
|
||||
|
||||
def define_hip_architectures(pkg: spack.package_base.PackageBase) -> str:
|
||||
"""Returns the str ``-DCMAKE_HIP_ARCHITECTURES:STRING=(expanded amdgpu_target)``.
|
||||
|
||||
``amdgpu_target`` is variant composed of a list of the target HIP
|
||||
architectures and it is declared in the rocm package.
|
||||
|
||||
This method is no-op for cmake<3.18 and when ``amdgpu_target`` variant is
|
||||
not set.
|
||||
|
||||
"""
|
||||
if "amdgpu_target" in pkg.spec.variants and pkg.spec.satisfies("^cmake@3.21:"):
|
||||
return define("CMAKE_HIP_ARCHITECTURES", pkg.spec.variants["amdgpu_target"].value)
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def define_cuda_architectures(pkg: spack.package_base.PackageBase) -> str:
|
||||
"""Returns the str ``-DCMAKE_CUDA_ARCHITECTURES:STRING=(expanded cuda_arch)``.
|
||||
|
||||
``cuda_arch`` is variant composed of a list of target CUDA architectures and
|
||||
it is declared in the cuda package.
|
||||
|
||||
This method is no-op for cmake<3.18 and when ``cuda_arch`` variant is not set.
|
||||
|
||||
"""
|
||||
if "cuda_arch" in pkg.spec.variants and pkg.spec.satisfies("^cmake@3.18:"):
|
||||
return define("CMAKE_CUDA_ARCHITECTURES", pkg.spec.variants["cuda_arch"].value)
|
||||
return ""
|
||||
|
@@ -7,8 +7,9 @@
|
||||
import spack.builder
|
||||
import spack.directives
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
|
||||
from ._checks import BaseBuilder, apply_macos_rpath_fixups, execute_install_time_tests
|
||||
from ._checks import BuilderWithDefaults, apply_macos_rpath_fixups, execute_install_time_tests
|
||||
|
||||
|
||||
class Package(spack.package_base.PackageBase):
|
||||
@@ -26,7 +27,7 @@ class Package(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("generic")
|
||||
class GenericBuilder(BaseBuilder):
|
||||
class GenericBuilder(BuilderWithDefaults):
|
||||
"""A builder for a generic build system, that require packagers
|
||||
to implement an "install" phase.
|
||||
"""
|
||||
@@ -44,7 +45,7 @@ class GenericBuilder(BaseBuilder):
|
||||
install_time_test_callbacks = []
|
||||
|
||||
# On macOS, force rpaths for shared library IDs and remove duplicate rpaths
|
||||
spack.builder.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
|
||||
spack.phase_callbacks.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
|
||||
|
||||
# unconditionally perform any post-install phase tests
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
@@ -7,10 +7,11 @@
|
||||
|
||||
import spack.builder
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
from spack.directives import build_system, extends
|
||||
from spack.multimethod import when
|
||||
|
||||
from ._checks import BaseBuilder, execute_install_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_install_time_tests
|
||||
|
||||
|
||||
class GoPackage(spack.package_base.PackageBase):
|
||||
@@ -32,7 +33,7 @@ class GoPackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("go")
|
||||
class GoBuilder(BaseBuilder):
|
||||
class GoBuilder(BuilderWithDefaults):
|
||||
"""The Go builder encodes the most common way of building software with
|
||||
a golang go.mod file. It has two phases that can be overridden, if need be:
|
||||
|
||||
@@ -99,7 +100,7 @@ def install(self, pkg, spec, prefix):
|
||||
fs.mkdirp(prefix.bin)
|
||||
fs.install(pkg.name, prefix.bin)
|
||||
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
||||
def check(self):
|
||||
"""Run ``go test .`` in the source directory"""
|
||||
|
@@ -22,8 +22,8 @@
|
||||
install,
|
||||
)
|
||||
|
||||
import spack.builder
|
||||
import spack.error
|
||||
import spack.phase_callbacks
|
||||
from spack.build_environment import dso_suffix
|
||||
from spack.error import InstallError
|
||||
from spack.util.environment import EnvironmentModifications
|
||||
@@ -1163,7 +1163,7 @@ def _determine_license_type(self):
|
||||
debug_print(license_type)
|
||||
return license_type
|
||||
|
||||
@spack.builder.run_before("install")
|
||||
@spack.phase_callbacks.run_before("install")
|
||||
def configure(self):
|
||||
"""Generates the silent.cfg file to pass to installer.sh.
|
||||
|
||||
@@ -1250,7 +1250,7 @@ def install(self, spec, prefix):
|
||||
for f in glob.glob("%s/intel*log" % tmpdir):
|
||||
install(f, dst)
|
||||
|
||||
@spack.builder.run_after("install")
|
||||
@spack.phase_callbacks.run_after("install")
|
||||
def validate_install(self):
|
||||
# Sometimes the installer exits with an error but doesn't pass a
|
||||
# non-zero exit code to spack. Check for the existence of a 'bin'
|
||||
@@ -1258,7 +1258,7 @@ def validate_install(self):
|
||||
if not os.path.exists(self.prefix.bin):
|
||||
raise InstallError("The installer has failed to install anything.")
|
||||
|
||||
@spack.builder.run_after("install")
|
||||
@spack.phase_callbacks.run_after("install")
|
||||
def configure_rpath(self):
|
||||
if "+rpath" not in self.spec:
|
||||
return
|
||||
@@ -1276,7 +1276,7 @@ def configure_rpath(self):
|
||||
with open(compiler_cfg, "w") as fh:
|
||||
fh.write("-Xlinker -rpath={0}\n".format(compilers_lib_dir))
|
||||
|
||||
@spack.builder.run_after("install")
|
||||
@spack.phase_callbacks.run_after("install")
|
||||
def configure_auto_dispatch(self):
|
||||
if self._has_compilers:
|
||||
if "auto_dispatch=none" in self.spec:
|
||||
@@ -1300,7 +1300,7 @@ def configure_auto_dispatch(self):
|
||||
with open(compiler_cfg, "a") as fh:
|
||||
fh.write("-ax{0}\n".format(",".join(ad)))
|
||||
|
||||
@spack.builder.run_after("install")
|
||||
@spack.phase_callbacks.run_after("install")
|
||||
def filter_compiler_wrappers(self):
|
||||
if ("+mpi" in self.spec or self.provides("mpi")) and "~newdtags" in self.spec:
|
||||
bin_dir = self.component_bin_dir("mpi")
|
||||
@@ -1308,7 +1308,7 @@ def filter_compiler_wrappers(self):
|
||||
f = os.path.join(bin_dir, f)
|
||||
filter_file("-Xlinker --enable-new-dtags", " ", f, string=True)
|
||||
|
||||
@spack.builder.run_after("install")
|
||||
@spack.phase_callbacks.run_after("install")
|
||||
def uninstall_ism(self):
|
||||
# The "Intel(R) Software Improvement Program" [ahem] gets installed,
|
||||
# apparently regardless of PHONEHOME_SEND_USAGE_DATA.
|
||||
@@ -1340,7 +1340,7 @@ def base_lib_dir(self):
|
||||
debug_print(d)
|
||||
return d
|
||||
|
||||
@spack.builder.run_after("install")
|
||||
@spack.phase_callbacks.run_after("install")
|
||||
def modify_LLVMgold_rpath(self):
|
||||
"""Add libimf.so and other required libraries to the RUNPATH of LLVMgold.so.
|
||||
|
||||
|
@@ -8,11 +8,14 @@
|
||||
|
||||
import spack.builder
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
import spack.spec
|
||||
import spack.util.prefix
|
||||
from spack.directives import build_system, conflicts, depends_on
|
||||
from spack.multimethod import when
|
||||
|
||||
from ._checks import (
|
||||
BaseBuilder,
|
||||
BuilderWithDefaults,
|
||||
apply_macos_rpath_fixups,
|
||||
execute_build_time_tests,
|
||||
execute_install_time_tests,
|
||||
@@ -36,7 +39,7 @@ class MakefilePackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("makefile")
|
||||
class MakefileBuilder(BaseBuilder):
|
||||
class MakefileBuilder(BuilderWithDefaults):
|
||||
"""The Makefile builder encodes the most common way of building software with
|
||||
Makefiles. It has three phases that can be overridden, if need be:
|
||||
|
||||
@@ -91,35 +94,50 @@ class MakefileBuilder(BaseBuilder):
|
||||
install_time_test_callbacks = ["installcheck"]
|
||||
|
||||
@property
|
||||
def build_directory(self):
|
||||
def build_directory(self) -> str:
|
||||
"""Return the directory containing the main Makefile."""
|
||||
return self.pkg.stage.source_path
|
||||
|
||||
def edit(self, pkg, spec, prefix):
|
||||
def edit(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Edit the Makefile before calling make. The default is a no-op."""
|
||||
pass
|
||||
|
||||
def build(self, pkg, spec, prefix):
|
||||
def build(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Run "make" on the build targets specified by the builder."""
|
||||
with fs.working_dir(self.build_directory):
|
||||
pkg.module.make(*self.build_targets)
|
||||
|
||||
def install(self, pkg, spec, prefix):
|
||||
def install(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Run "make" on the install targets specified by the builder."""
|
||||
with fs.working_dir(self.build_directory):
|
||||
pkg.module.make(*self.install_targets)
|
||||
|
||||
spack.builder.run_after("build")(execute_build_time_tests)
|
||||
spack.phase_callbacks.run_after("build")(execute_build_time_tests)
|
||||
|
||||
def check(self):
|
||||
def check(self) -> None:
|
||||
"""Run "make" on the ``test`` and ``check`` targets, if found."""
|
||||
with fs.working_dir(self.build_directory):
|
||||
self.pkg._if_make_target_execute("test")
|
||||
self.pkg._if_make_target_execute("check")
|
||||
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
||||
def installcheck(self):
|
||||
def installcheck(self) -> None:
|
||||
"""Searches the Makefile for an ``installcheck`` target
|
||||
and runs it if found.
|
||||
"""
|
||||
@@ -127,4 +145,4 @@ def installcheck(self):
|
||||
self.pkg._if_make_target_execute("installcheck")
|
||||
|
||||
# On macOS, force rpaths for shared library IDs and remove duplicate rpaths
|
||||
spack.builder.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
|
||||
spack.phase_callbacks.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
|
||||
|
@@ -10,7 +10,7 @@
|
||||
from spack.multimethod import when
|
||||
from spack.util.executable import which
|
||||
|
||||
from ._checks import BaseBuilder
|
||||
from ._checks import BuilderWithDefaults
|
||||
|
||||
|
||||
class MavenPackage(spack.package_base.PackageBase):
|
||||
@@ -34,7 +34,7 @@ class MavenPackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("maven")
|
||||
class MavenBuilder(BaseBuilder):
|
||||
class MavenBuilder(BuilderWithDefaults):
|
||||
"""The Maven builder encodes the default way to build software with Maven.
|
||||
It has two phases that can be overridden, if need be:
|
||||
|
||||
|
@@ -9,10 +9,13 @@
|
||||
|
||||
import spack.builder
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
import spack.spec
|
||||
import spack.util.prefix
|
||||
from spack.directives import build_system, conflicts, depends_on, variant
|
||||
from spack.multimethod import when
|
||||
|
||||
from ._checks import BaseBuilder, execute_build_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
||||
|
||||
|
||||
class MesonPackage(spack.package_base.PackageBase):
|
||||
@@ -62,7 +65,7 @@ def flags_to_build_system_args(self, flags):
|
||||
|
||||
|
||||
@spack.builder.builder("meson")
|
||||
class MesonBuilder(BaseBuilder):
|
||||
class MesonBuilder(BuilderWithDefaults):
|
||||
"""The Meson builder encodes the default way to build software with Meson.
|
||||
The builder has three phases that can be overridden, if need be:
|
||||
|
||||
@@ -112,7 +115,7 @@ def archive_files(self):
|
||||
return [os.path.join(self.build_directory, "meson-logs", "meson-log.txt")]
|
||||
|
||||
@property
|
||||
def root_mesonlists_dir(self):
|
||||
def root_mesonlists_dir(self) -> str:
|
||||
"""Relative path to the directory containing meson.build
|
||||
|
||||
This path is relative to the root of the extracted tarball,
|
||||
@@ -121,7 +124,7 @@ def root_mesonlists_dir(self):
|
||||
return self.pkg.stage.source_path
|
||||
|
||||
@property
|
||||
def std_meson_args(self):
|
||||
def std_meson_args(self) -> List[str]:
|
||||
"""Standard meson arguments provided as a property for convenience
|
||||
of package writers.
|
||||
"""
|
||||
@@ -132,7 +135,7 @@ def std_meson_args(self):
|
||||
return std_meson_args
|
||||
|
||||
@staticmethod
|
||||
def std_args(pkg):
|
||||
def std_args(pkg) -> List[str]:
|
||||
"""Standard meson arguments for a generic package."""
|
||||
try:
|
||||
build_type = pkg.spec.variants["buildtype"].value
|
||||
@@ -172,7 +175,7 @@ def build_directory(self):
|
||||
"""Directory to use when building the package."""
|
||||
return os.path.join(self.pkg.stage.path, self.build_dirname)
|
||||
|
||||
def meson_args(self):
|
||||
def meson_args(self) -> List[str]:
|
||||
"""List of arguments that must be passed to meson, except:
|
||||
|
||||
* ``--prefix``
|
||||
@@ -185,7 +188,12 @@ def meson_args(self):
|
||||
"""
|
||||
return []
|
||||
|
||||
def meson(self, pkg, spec, prefix):
|
||||
def meson(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Run ``meson`` in the build directory"""
|
||||
options = []
|
||||
if self.spec["meson"].satisfies("@0.64:"):
|
||||
@@ -196,21 +204,31 @@ def meson(self, pkg, spec, prefix):
|
||||
with fs.working_dir(self.build_directory, create=True):
|
||||
pkg.module.meson(*options)
|
||||
|
||||
def build(self, pkg, spec, prefix):
|
||||
def build(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Make the build targets"""
|
||||
options = ["-v"]
|
||||
options += self.build_targets
|
||||
with fs.working_dir(self.build_directory):
|
||||
pkg.module.ninja(*options)
|
||||
|
||||
def install(self, pkg, spec, prefix):
|
||||
def install(
|
||||
self,
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
prefix: spack.util.prefix.Prefix,
|
||||
) -> None:
|
||||
"""Make the install targets"""
|
||||
with fs.working_dir(self.build_directory):
|
||||
pkg.module.ninja(*self.install_targets)
|
||||
|
||||
spack.builder.run_after("build")(execute_build_time_tests)
|
||||
spack.phase_callbacks.run_after("build")(execute_build_time_tests)
|
||||
|
||||
def check(self):
|
||||
def check(self) -> None:
|
||||
"""Search Meson-generated files for the target ``test`` and run it if found."""
|
||||
with fs.working_dir(self.build_directory):
|
||||
self.pkg._if_ninja_target_execute("test")
|
||||
|
@@ -10,7 +10,7 @@
|
||||
import spack.package_base
|
||||
from spack.directives import build_system, conflicts
|
||||
|
||||
from ._checks import BaseBuilder
|
||||
from ._checks import BuilderWithDefaults
|
||||
|
||||
|
||||
class MSBuildPackage(spack.package_base.PackageBase):
|
||||
@@ -26,7 +26,7 @@ class MSBuildPackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("msbuild")
|
||||
class MSBuildBuilder(BaseBuilder):
|
||||
class MSBuildBuilder(BuilderWithDefaults):
|
||||
"""The MSBuild builder encodes the most common way of building software with
|
||||
Mircosoft's MSBuild tool. It has two phases that can be overridden, if need be:
|
||||
|
||||
|
@@ -10,7 +10,7 @@
|
||||
import spack.package_base
|
||||
from spack.directives import build_system, conflicts
|
||||
|
||||
from ._checks import BaseBuilder
|
||||
from ._checks import BuilderWithDefaults
|
||||
|
||||
|
||||
class NMakePackage(spack.package_base.PackageBase):
|
||||
@@ -26,7 +26,7 @@ class NMakePackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("nmake")
|
||||
class NMakeBuilder(BaseBuilder):
|
||||
class NMakeBuilder(BuilderWithDefaults):
|
||||
"""The NMake builder encodes the most common way of building software with
|
||||
Mircosoft's NMake tool. It has two phases that can be overridden, if need be:
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
from spack.directives import build_system, extends
|
||||
from spack.multimethod import when
|
||||
|
||||
from ._checks import BaseBuilder
|
||||
from ._checks import BuilderWithDefaults
|
||||
|
||||
|
||||
class OctavePackage(spack.package_base.PackageBase):
|
||||
@@ -29,7 +29,7 @@ class OctavePackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("octave")
|
||||
class OctaveBuilder(BaseBuilder):
|
||||
class OctaveBuilder(BuilderWithDefaults):
|
||||
"""The octave builder provides the following phases that can be overridden:
|
||||
|
||||
1. :py:meth:`~.OctaveBuilder.install`
|
||||
|
@@ -10,11 +10,12 @@
|
||||
|
||||
import spack.builder
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
from spack.directives import build_system, extends
|
||||
from spack.install_test import SkipTest, test_part
|
||||
from spack.util.executable import Executable
|
||||
|
||||
from ._checks import BaseBuilder, execute_build_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
||||
|
||||
|
||||
class PerlPackage(spack.package_base.PackageBase):
|
||||
@@ -84,7 +85,7 @@ def test_use(self):
|
||||
|
||||
|
||||
@spack.builder.builder("perl")
|
||||
class PerlBuilder(BaseBuilder):
|
||||
class PerlBuilder(BuilderWithDefaults):
|
||||
"""The perl builder provides four phases that can be overridden, if required:
|
||||
|
||||
1. :py:meth:`~.PerlBuilder.configure`
|
||||
@@ -163,7 +164,7 @@ def configure(self, pkg, spec, prefix):
|
||||
# Build.PL may be too long causing the build to fail. Patching the shebang
|
||||
# does not happen until after install so set '/usr/bin/env perl' here in
|
||||
# the Build script.
|
||||
@spack.builder.run_after("configure")
|
||||
@spack.phase_callbacks.run_after("configure")
|
||||
def fix_shebang(self):
|
||||
if self.build_method == "Build.PL":
|
||||
pattern = "#!{0}".format(self.spec["perl"].command.path)
|
||||
@@ -175,7 +176,7 @@ def build(self, pkg, spec, prefix):
|
||||
self.build_executable()
|
||||
|
||||
# Ensure that tests run after build (if requested):
|
||||
spack.builder.run_after("build")(execute_build_time_tests)
|
||||
spack.phase_callbacks.run_after("build")(execute_build_time_tests)
|
||||
|
||||
def check(self):
|
||||
"""Runs built-in tests of a Perl package."""
|
||||
|
@@ -24,6 +24,7 @@
|
||||
import spack.detection
|
||||
import spack.multimethod
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
import spack.platforms
|
||||
import spack.repo
|
||||
import spack.spec
|
||||
@@ -34,7 +35,7 @@
|
||||
from spack.spec import Spec
|
||||
from spack.util.prefix import Prefix
|
||||
|
||||
from ._checks import BaseBuilder, execute_install_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_install_time_tests
|
||||
|
||||
|
||||
def _flatten_dict(dictionary: Mapping[str, object]) -> Iterable[str]:
|
||||
@@ -374,7 +375,7 @@ def list_url(cls) -> Optional[str]: # type: ignore[override]
|
||||
return None
|
||||
|
||||
@property
|
||||
def python_spec(self):
|
||||
def python_spec(self) -> Spec:
|
||||
"""Get python-venv if it exists or python otherwise."""
|
||||
python, *_ = self.spec.dependencies("python-venv") or self.spec.dependencies("python")
|
||||
return python
|
||||
@@ -425,7 +426,7 @@ def libs(self) -> LibraryList:
|
||||
|
||||
|
||||
@spack.builder.builder("python_pip")
|
||||
class PythonPipBuilder(BaseBuilder):
|
||||
class PythonPipBuilder(BuilderWithDefaults):
|
||||
phases = ("install",)
|
||||
|
||||
#: Names associated with package methods in the old build-system format
|
||||
@@ -543,4 +544,4 @@ def install(self, pkg: PythonPackage, spec: Spec, prefix: Prefix) -> None:
|
||||
with fs.working_dir(self.build_directory):
|
||||
pip(*args)
|
||||
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
@@ -6,9 +6,10 @@
|
||||
|
||||
import spack.builder
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
from spack.directives import build_system, depends_on
|
||||
|
||||
from ._checks import BaseBuilder, execute_build_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
||||
|
||||
|
||||
class QMakePackage(spack.package_base.PackageBase):
|
||||
@@ -30,7 +31,7 @@ class QMakePackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("qmake")
|
||||
class QMakeBuilder(BaseBuilder):
|
||||
class QMakeBuilder(BuilderWithDefaults):
|
||||
"""The qmake builder provides three phases that can be overridden:
|
||||
|
||||
1. :py:meth:`~.QMakeBuilder.qmake`
|
||||
@@ -81,4 +82,4 @@ def check(self):
|
||||
with working_dir(self.build_directory):
|
||||
self.pkg._if_make_target_execute("check")
|
||||
|
||||
spack.builder.run_after("build")(execute_build_time_tests)
|
||||
spack.phase_callbacks.run_after("build")(execute_build_time_tests)
|
||||
|
@@ -8,7 +8,7 @@
|
||||
import spack.package_base
|
||||
from spack.directives import build_system, extends, maintainers
|
||||
|
||||
from ._checks import BaseBuilder
|
||||
from ._checks import BuilderWithDefaults
|
||||
|
||||
|
||||
class RubyPackage(spack.package_base.PackageBase):
|
||||
@@ -28,7 +28,7 @@ class RubyPackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("ruby")
|
||||
class RubyBuilder(BaseBuilder):
|
||||
class RubyBuilder(BuilderWithDefaults):
|
||||
"""The Ruby builder provides two phases that can be overridden if required:
|
||||
|
||||
#. :py:meth:`~.RubyBuilder.build`
|
||||
|
@@ -4,9 +4,10 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
import spack.builder
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
from spack.directives import build_system, depends_on
|
||||
|
||||
from ._checks import BaseBuilder, execute_build_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
||||
|
||||
|
||||
class SConsPackage(spack.package_base.PackageBase):
|
||||
@@ -28,7 +29,7 @@ class SConsPackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("scons")
|
||||
class SConsBuilder(BaseBuilder):
|
||||
class SConsBuilder(BuilderWithDefaults):
|
||||
"""The Scons builder provides the following phases that can be overridden:
|
||||
|
||||
1. :py:meth:`~.SConsBuilder.build`
|
||||
@@ -79,4 +80,4 @@ def build_test(self):
|
||||
"""
|
||||
pass
|
||||
|
||||
spack.builder.run_after("build")(execute_build_time_tests)
|
||||
spack.phase_callbacks.run_after("build")(execute_build_time_tests)
|
||||
|
@@ -11,11 +11,12 @@
|
||||
import spack.builder
|
||||
import spack.install_test
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
from spack.directives import build_system, depends_on, extends
|
||||
from spack.multimethod import when
|
||||
from spack.util.executable import Executable
|
||||
|
||||
from ._checks import BaseBuilder, execute_install_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_install_time_tests
|
||||
|
||||
|
||||
class SIPPackage(spack.package_base.PackageBase):
|
||||
@@ -103,7 +104,7 @@ def test_imports(self):
|
||||
|
||||
|
||||
@spack.builder.builder("sip")
|
||||
class SIPBuilder(BaseBuilder):
|
||||
class SIPBuilder(BuilderWithDefaults):
|
||||
"""The SIP builder provides the following phases that can be overridden:
|
||||
|
||||
* configure
|
||||
@@ -170,4 +171,4 @@ def install_args(self):
|
||||
"""Arguments to pass to install."""
|
||||
return []
|
||||
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
@@ -6,9 +6,10 @@
|
||||
|
||||
import spack.builder
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
from spack.directives import build_system, depends_on
|
||||
|
||||
from ._checks import BaseBuilder, execute_build_time_tests, execute_install_time_tests
|
||||
from ._checks import BuilderWithDefaults, execute_build_time_tests, execute_install_time_tests
|
||||
|
||||
|
||||
class WafPackage(spack.package_base.PackageBase):
|
||||
@@ -30,7 +31,7 @@ class WafPackage(spack.package_base.PackageBase):
|
||||
|
||||
|
||||
@spack.builder.builder("waf")
|
||||
class WafBuilder(BaseBuilder):
|
||||
class WafBuilder(BuilderWithDefaults):
|
||||
"""The WAF builder provides the following phases that can be overridden:
|
||||
|
||||
* configure
|
||||
@@ -136,7 +137,7 @@ def build_test(self):
|
||||
"""
|
||||
pass
|
||||
|
||||
spack.builder.run_after("build")(execute_build_time_tests)
|
||||
spack.phase_callbacks.run_after("build")(execute_build_time_tests)
|
||||
|
||||
def install_test(self):
|
||||
"""Run unit tests after install.
|
||||
@@ -146,4 +147,4 @@ def install_test(self):
|
||||
"""
|
||||
pass
|
||||
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
@@ -6,44 +6,30 @@
|
||||
import collections.abc
|
||||
import copy
|
||||
import functools
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from llnl.util import lang
|
||||
from typing import Dict, List, Optional, Tuple, Type
|
||||
|
||||
import spack.error
|
||||
import spack.multimethod
|
||||
import spack.package_base
|
||||
import spack.phase_callbacks
|
||||
import spack.repo
|
||||
import spack.spec
|
||||
import spack.util.environment
|
||||
|
||||
#: Builder classes, as registered by the "builder" decorator
|
||||
BUILDER_CLS = {}
|
||||
|
||||
#: An object of this kind is a shared global state used to collect callbacks during
|
||||
#: class definition time, and is flushed when the class object is created at the end
|
||||
#: of the class definition
|
||||
#:
|
||||
#: Args:
|
||||
#: attribute_name (str): name of the attribute that will be attached to the builder
|
||||
#: callbacks (list): container used to temporarily aggregate the callbacks
|
||||
CallbackTemporaryStage = collections.namedtuple(
|
||||
"CallbackTemporaryStage", ["attribute_name", "callbacks"]
|
||||
)
|
||||
|
||||
#: Shared global state to aggregate "@run_before" callbacks
|
||||
_RUN_BEFORE = CallbackTemporaryStage(attribute_name="run_before_callbacks", callbacks=[])
|
||||
#: Shared global state to aggregate "@run_after" callbacks
|
||||
_RUN_AFTER = CallbackTemporaryStage(attribute_name="run_after_callbacks", callbacks=[])
|
||||
BUILDER_CLS: Dict[str, Type["Builder"]] = {}
|
||||
|
||||
#: Map id(pkg) to a builder, to avoid creating multiple
|
||||
#: builders for the same package object.
|
||||
_BUILDERS = {}
|
||||
_BUILDERS: Dict[int, "Builder"] = {}
|
||||
|
||||
|
||||
def builder(build_system_name):
|
||||
def builder(build_system_name: str):
|
||||
"""Class decorator used to register the default builder
|
||||
for a given build-system.
|
||||
|
||||
Args:
|
||||
build_system_name (str): name of the build-system
|
||||
build_system_name: name of the build-system
|
||||
"""
|
||||
|
||||
def _decorator(cls):
|
||||
@@ -54,13 +40,9 @@ def _decorator(cls):
|
||||
return _decorator
|
||||
|
||||
|
||||
def create(pkg):
|
||||
"""Given a package object with an associated concrete spec,
|
||||
return the builder object that can install it.
|
||||
|
||||
Args:
|
||||
pkg (spack.package_base.PackageBase): package for which we want the builder
|
||||
"""
|
||||
def create(pkg: spack.package_base.PackageBase) -> "Builder":
|
||||
"""Given a package object with an associated concrete spec, return the builder object that can
|
||||
install it."""
|
||||
if id(pkg) not in _BUILDERS:
|
||||
_BUILDERS[id(pkg)] = _create(pkg)
|
||||
return _BUILDERS[id(pkg)]
|
||||
@@ -75,7 +57,7 @@ def __call__(self, spec, prefix):
|
||||
return self.phase_fn(self.builder.pkg, spec, prefix)
|
||||
|
||||
|
||||
def get_builder_class(pkg, name: str) -> Optional[type]:
|
||||
def get_builder_class(pkg, name: str) -> Optional[Type["Builder"]]:
|
||||
"""Return the builder class if a package module defines it."""
|
||||
cls = getattr(pkg.module, name, None)
|
||||
if cls and cls.__module__.startswith(spack.repo.ROOT_PYTHON_NAMESPACE):
|
||||
@@ -83,7 +65,7 @@ def get_builder_class(pkg, name: str) -> Optional[type]:
|
||||
return None
|
||||
|
||||
|
||||
def _create(pkg):
|
||||
def _create(pkg: spack.package_base.PackageBase) -> "Builder":
|
||||
"""Return a new builder object for the package object being passed as argument.
|
||||
|
||||
The function inspects the build-system used by the package object and try to:
|
||||
@@ -103,7 +85,7 @@ class hierarchy (look at AspellDictPackage for an example of that)
|
||||
to look for build-related methods in the ``*Package``.
|
||||
|
||||
Args:
|
||||
pkg (spack.package_base.PackageBase): package object for which we need a builder
|
||||
pkg: package object for which we need a builder
|
||||
"""
|
||||
package_buildsystem = buildsystem_name(pkg)
|
||||
default_builder_cls = BUILDER_CLS[package_buildsystem]
|
||||
@@ -168,8 +150,8 @@ def __forward(self, *args, **kwargs):
|
||||
# with the same name is defined in the Package, it will override this definition
|
||||
# (when _ForwardToBaseBuilder is initialized)
|
||||
for method_name in (
|
||||
base_cls.phases
|
||||
+ base_cls.legacy_methods
|
||||
base_cls.phases # type: ignore
|
||||
+ base_cls.legacy_methods # type: ignore
|
||||
+ getattr(base_cls, "legacy_long_methods", tuple())
|
||||
+ ("setup_build_environment", "setup_dependent_build_environment")
|
||||
):
|
||||
@@ -181,14 +163,14 @@ def __forward(self):
|
||||
|
||||
return __forward
|
||||
|
||||
for attribute_name in base_cls.legacy_attributes:
|
||||
for attribute_name in base_cls.legacy_attributes: # type: ignore
|
||||
setattr(
|
||||
_ForwardToBaseBuilder,
|
||||
attribute_name,
|
||||
property(forward_property_to_getattr(attribute_name)),
|
||||
)
|
||||
|
||||
class Adapter(base_cls, metaclass=_PackageAdapterMeta):
|
||||
class Adapter(base_cls, metaclass=_PackageAdapterMeta): # type: ignore
|
||||
def __init__(self, pkg):
|
||||
# Deal with custom phases in packages here
|
||||
if hasattr(pkg, "phases"):
|
||||
@@ -213,99 +195,18 @@ def setup_dependent_build_environment(self, env, dependent_spec):
|
||||
return Adapter(pkg)
|
||||
|
||||
|
||||
def buildsystem_name(pkg):
|
||||
def buildsystem_name(pkg: spack.package_base.PackageBase) -> str:
|
||||
"""Given a package object with an associated concrete spec,
|
||||
return the name of its build system.
|
||||
|
||||
Args:
|
||||
pkg (spack.package_base.PackageBase): package for which we want
|
||||
the build system name
|
||||
"""
|
||||
return the name of its build system."""
|
||||
try:
|
||||
return pkg.spec.variants["build_system"].value
|
||||
except KeyError:
|
||||
# We are reading an old spec without the build_system variant
|
||||
return pkg.legacy_buildsystem
|
||||
|
||||
|
||||
class PhaseCallbacksMeta(type):
|
||||
"""Permit to register arbitrary functions during class definition and run them
|
||||
later, before or after a given install phase.
|
||||
|
||||
Each method decorated with ``run_before`` or ``run_after`` gets temporarily
|
||||
stored in a global shared state when a class being defined is parsed by the Python
|
||||
interpreter. At class definition time that temporary storage gets flushed and a list
|
||||
of callbacks is attached to the class being defined.
|
||||
"""
|
||||
|
||||
def __new__(mcs, name, bases, attr_dict):
|
||||
for temporary_stage in (_RUN_BEFORE, _RUN_AFTER):
|
||||
staged_callbacks = temporary_stage.callbacks
|
||||
|
||||
# Here we have an adapter from an old-style package. This means there is no
|
||||
# hierarchy of builders, and every callback that had to be combined between
|
||||
# *Package and *Builder has been combined already by _PackageAdapterMeta
|
||||
if name == "Adapter":
|
||||
continue
|
||||
|
||||
# If we are here we have callbacks. To get a complete list, we accumulate all the
|
||||
# callbacks from base classes, we deduplicate them, then prepend what we have
|
||||
# registered here.
|
||||
#
|
||||
# The order should be:
|
||||
# 1. Callbacks are registered in order within the same class
|
||||
# 2. Callbacks defined in derived classes precede those defined in base
|
||||
# classes
|
||||
callbacks_from_base = []
|
||||
for base in bases:
|
||||
current_callbacks = getattr(base, temporary_stage.attribute_name, None)
|
||||
if not current_callbacks:
|
||||
continue
|
||||
callbacks_from_base.extend(current_callbacks)
|
||||
callbacks_from_base = list(lang.dedupe(callbacks_from_base))
|
||||
# Set the callbacks in this class and flush the temporary stage
|
||||
attr_dict[temporary_stage.attribute_name] = staged_callbacks[:] + callbacks_from_base
|
||||
del temporary_stage.callbacks[:]
|
||||
|
||||
return super(PhaseCallbacksMeta, mcs).__new__(mcs, name, bases, attr_dict)
|
||||
|
||||
@staticmethod
|
||||
def run_after(phase, when=None):
|
||||
"""Decorator to register a function for running after a given phase.
|
||||
|
||||
Args:
|
||||
phase (str): phase after which the function must run.
|
||||
when (str): condition under which the function is run (if None, it is always run).
|
||||
"""
|
||||
|
||||
def _decorator(fn):
|
||||
key = (phase, when)
|
||||
item = (key, fn)
|
||||
_RUN_AFTER.callbacks.append(item)
|
||||
return fn
|
||||
|
||||
return _decorator
|
||||
|
||||
@staticmethod
|
||||
def run_before(phase, when=None):
|
||||
"""Decorator to register a function for running before a given phase.
|
||||
|
||||
Args:
|
||||
phase (str): phase before which the function must run.
|
||||
when (str): condition under which the function is run (if None, it is always run).
|
||||
"""
|
||||
|
||||
def _decorator(fn):
|
||||
key = (phase, when)
|
||||
item = (key, fn)
|
||||
_RUN_BEFORE.callbacks.append(item)
|
||||
return fn
|
||||
|
||||
return _decorator
|
||||
return pkg.legacy_buildsystem # type: ignore
|
||||
|
||||
|
||||
class BuilderMeta(
|
||||
PhaseCallbacksMeta,
|
||||
spack.phase_callbacks.PhaseCallbacksMeta,
|
||||
spack.multimethod.MultiMethodMeta,
|
||||
type(collections.abc.Sequence), # type: ignore
|
||||
):
|
||||
@@ -400,8 +301,12 @@ def __new__(mcs, name, bases, attr_dict):
|
||||
)
|
||||
|
||||
combine_callbacks = _PackageAdapterMeta.combine_callbacks
|
||||
attr_dict[_RUN_BEFORE.attribute_name] = combine_callbacks(_RUN_BEFORE.attribute_name)
|
||||
attr_dict[_RUN_AFTER.attribute_name] = combine_callbacks(_RUN_AFTER.attribute_name)
|
||||
attr_dict[spack.phase_callbacks._RUN_BEFORE.attribute_name] = combine_callbacks(
|
||||
spack.phase_callbacks._RUN_BEFORE.attribute_name
|
||||
)
|
||||
attr_dict[spack.phase_callbacks._RUN_AFTER.attribute_name] = combine_callbacks(
|
||||
spack.phase_callbacks._RUN_AFTER.attribute_name
|
||||
)
|
||||
|
||||
return super(_PackageAdapterMeta, mcs).__new__(mcs, name, bases, attr_dict)
|
||||
|
||||
@@ -421,8 +326,8 @@ def __init__(self, name, builder):
|
||||
self.name = name
|
||||
self.builder = builder
|
||||
self.phase_fn = self._select_phase_fn()
|
||||
self.run_before = self._make_callbacks(_RUN_BEFORE.attribute_name)
|
||||
self.run_after = self._make_callbacks(_RUN_AFTER.attribute_name)
|
||||
self.run_before = self._make_callbacks(spack.phase_callbacks._RUN_BEFORE.attribute_name)
|
||||
self.run_after = self._make_callbacks(spack.phase_callbacks._RUN_AFTER.attribute_name)
|
||||
|
||||
def _make_callbacks(self, callbacks_attribute):
|
||||
result = []
|
||||
@@ -483,15 +388,103 @@ def copy(self):
|
||||
return copy.deepcopy(self)
|
||||
|
||||
|
||||
class Builder(collections.abc.Sequence, metaclass=BuilderMeta):
|
||||
"""A builder is a class that, given a package object (i.e. associated with
|
||||
concrete spec), knows how to install it.
|
||||
class BaseBuilder(metaclass=BuilderMeta):
|
||||
"""An interface for builders, without any phases defined. This class is exposed in the package
|
||||
API, so that packagers can create a single class to define ``setup_build_environment`` and
|
||||
``@run_before`` and ``@run_after`` callbacks that can be shared among different builders.
|
||||
|
||||
The builder behaves like a sequence, and when iterated over return the
|
||||
"phases" of the installation in the correct order.
|
||||
Example:
|
||||
|
||||
Args:
|
||||
pkg (spack.package_base.PackageBase): package object to be built
|
||||
.. code-block:: python
|
||||
|
||||
class AnyBuilder(BaseBuilder):
|
||||
@run_after("install")
|
||||
def fixup_install(self):
|
||||
# do something after the package is installed
|
||||
pass
|
||||
|
||||
def setup_build_environment(self, env):
|
||||
env.set("MY_ENV_VAR", "my_value")
|
||||
|
||||
class CMakeBuilder(cmake.CMakeBuilder, AnyBuilder):
|
||||
pass
|
||||
|
||||
class AutotoolsBuilder(autotools.AutotoolsBuilder, AnyBuilder):
|
||||
pass
|
||||
"""
|
||||
|
||||
def __init__(self, pkg: spack.package_base.PackageBase) -> None:
|
||||
self.pkg = pkg
|
||||
|
||||
@property
|
||||
def spec(self) -> spack.spec.Spec:
|
||||
return self.pkg.spec
|
||||
|
||||
@property
|
||||
def stage(self):
|
||||
return self.pkg.stage
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
return self.pkg.prefix
|
||||
|
||||
def setup_build_environment(
|
||||
self, env: spack.util.environment.EnvironmentModifications
|
||||
) -> None:
|
||||
"""Sets up the build environment for a package.
|
||||
|
||||
This method will be called before the current package prefix exists in
|
||||
Spack's store.
|
||||
|
||||
Args:
|
||||
env: environment modifications to be applied when the package is built. Package authors
|
||||
can call methods on it to alter the build environment.
|
||||
"""
|
||||
if not hasattr(super(), "setup_build_environment"):
|
||||
return
|
||||
super().setup_build_environment(env) # type: ignore
|
||||
|
||||
def setup_dependent_build_environment(
|
||||
self, env: spack.util.environment.EnvironmentModifications, dependent_spec: spack.spec.Spec
|
||||
) -> None:
|
||||
"""Sets up the build environment of a package that depends on this one.
|
||||
|
||||
This is similar to ``setup_build_environment``, but it is used to modify the build
|
||||
environment of a package that *depends* on this one.
|
||||
|
||||
This gives packages the ability to set environment variables for the build of the
|
||||
dependent, which can be useful to provide search hints for headers or libraries if they are
|
||||
not in standard locations.
|
||||
|
||||
This method will be called before the dependent package prefix exists in Spack's store.
|
||||
|
||||
Args:
|
||||
env: environment modifications to be applied when the dependent package is built.
|
||||
Package authors can call methods on it to alter the build environment.
|
||||
|
||||
dependent_spec: the spec of the dependent package about to be built. This allows the
|
||||
extendee (self) to query the dependent's state. Note that *this* package's spec is
|
||||
available as ``self.spec``
|
||||
"""
|
||||
if not hasattr(super(), "setup_dependent_build_environment"):
|
||||
return
|
||||
super().setup_dependent_build_environment(env, dependent_spec) # type: ignore
|
||||
|
||||
def __repr__(self):
|
||||
fmt = "{name}{/hash:7}"
|
||||
return f"{self.__class__.__name__}({self.spec.format(fmt)})"
|
||||
|
||||
def __str__(self):
|
||||
fmt = "{name}{/hash:7}"
|
||||
return f'"{self.__class__.__name__}" builder for "{self.spec.format(fmt)}"'
|
||||
|
||||
|
||||
class Builder(BaseBuilder, collections.abc.Sequence):
|
||||
"""A builder is a class that, given a package object (i.e. associated with concrete spec),
|
||||
knows how to install it.
|
||||
|
||||
The builder behaves like a sequence, and when iterated over return the "phases" of the
|
||||
installation in the correct order.
|
||||
"""
|
||||
|
||||
#: Sequence of phases. Must be defined in derived classes
|
||||
@@ -506,95 +499,22 @@ class Builder(collections.abc.Sequence, metaclass=BuilderMeta):
|
||||
build_time_test_callbacks: List[str]
|
||||
install_time_test_callbacks: List[str]
|
||||
|
||||
#: List of glob expressions. Each expression must either be
|
||||
#: absolute or relative to the package source path.
|
||||
#: Matching artifacts found at the end of the build process will be
|
||||
#: copied in the same directory tree as _spack_build_logfile and
|
||||
#: _spack_build_envfile.
|
||||
archive_files: List[str] = []
|
||||
#: List of glob expressions. Each expression must either be absolute or relative to the package
|
||||
#: source path. Matching artifacts found at the end of the build process will be copied in the
|
||||
#: same directory tree as _spack_build_logfile and _spack_build_envfile.
|
||||
@property
|
||||
def archive_files(self) -> List[str]:
|
||||
return []
|
||||
|
||||
def __init__(self, pkg):
|
||||
self.pkg = pkg
|
||||
def __init__(self, pkg: spack.package_base.PackageBase) -> None:
|
||||
super().__init__(pkg)
|
||||
self.callbacks = {}
|
||||
for phase in self.phases:
|
||||
self.callbacks[phase] = InstallationPhase(phase, self)
|
||||
|
||||
@property
|
||||
def spec(self):
|
||||
return self.pkg.spec
|
||||
|
||||
@property
|
||||
def stage(self):
|
||||
return self.pkg.stage
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
return self.pkg.prefix
|
||||
|
||||
def setup_build_environment(self, env):
|
||||
"""Sets up the build environment for a package.
|
||||
|
||||
This method will be called before the current package prefix exists in
|
||||
Spack's store.
|
||||
|
||||
Args:
|
||||
env (spack.util.environment.EnvironmentModifications): environment
|
||||
modifications to be applied when the package is built. Package authors
|
||||
can call methods on it to alter the build environment.
|
||||
"""
|
||||
if not hasattr(super(), "setup_build_environment"):
|
||||
return
|
||||
super().setup_build_environment(env)
|
||||
|
||||
def setup_dependent_build_environment(self, env, dependent_spec):
|
||||
"""Sets up the build environment of packages that depend on this one.
|
||||
|
||||
This is similar to ``setup_build_environment``, but it is used to
|
||||
modify the build environments of packages that *depend* on this one.
|
||||
|
||||
This gives packages like Python and others that follow the extension
|
||||
model a way to implement common environment or compile-time settings
|
||||
for dependencies.
|
||||
|
||||
This method will be called before the dependent package prefix exists
|
||||
in Spack's store.
|
||||
|
||||
Examples:
|
||||
1. Installing python modules generally requires ``PYTHONPATH``
|
||||
to point to the ``lib/pythonX.Y/site-packages`` directory in the
|
||||
module's install prefix. This method could be used to set that
|
||||
variable.
|
||||
|
||||
Args:
|
||||
env (spack.util.environment.EnvironmentModifications): environment
|
||||
modifications to be applied when the dependent package is built.
|
||||
Package authors can call methods on it to alter the build environment.
|
||||
|
||||
dependent_spec (spack.spec.Spec): the spec of the dependent package
|
||||
about to be built. This allows the extendee (self) to query
|
||||
the dependent's state. Note that *this* package's spec is
|
||||
available as ``self.spec``
|
||||
"""
|
||||
if not hasattr(super(), "setup_dependent_build_environment"):
|
||||
return
|
||||
super().setup_dependent_build_environment(env, dependent_spec)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
key = self.phases[idx]
|
||||
return self.callbacks[key]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.phases)
|
||||
|
||||
def __repr__(self):
|
||||
msg = "{0}({1})"
|
||||
return msg.format(type(self).__name__, self.pkg.spec.format("{name}/{hash:7}"))
|
||||
|
||||
def __str__(self):
|
||||
msg = '"{0}" builder for "{1}"'
|
||||
return msg.format(type(self).build_system, self.pkg.spec.format("{name}/{hash:7}"))
|
||||
|
||||
|
||||
# Export these names as standalone to be used in packages
|
||||
run_after = PhaseCallbacksMeta.run_after
|
||||
run_before = PhaseCallbacksMeta.run_before
|
||||
|
@@ -32,6 +32,7 @@
|
||||
|
||||
import spack
|
||||
import spack.binary_distribution as bindist
|
||||
import spack.builder
|
||||
import spack.concretize
|
||||
import spack.config as cfg
|
||||
import spack.error
|
||||
@@ -1387,7 +1388,11 @@ def copy_stage_logs_to_artifacts(job_spec: spack.spec.Spec, job_log_dir: str) ->
|
||||
|
||||
stage_dir = job_pkg.stage.path
|
||||
tty.debug(f"stage dir: {stage_dir}")
|
||||
for file in [job_pkg.log_path, job_pkg.env_mods_path, *job_pkg.builder.archive_files]:
|
||||
for file in [
|
||||
job_pkg.log_path,
|
||||
job_pkg.env_mods_path,
|
||||
*spack.builder.create(job_pkg).archive_files,
|
||||
]:
|
||||
copy_files_to_artifacts(file, job_log_dir)
|
||||
|
||||
|
||||
|
@@ -11,6 +11,7 @@
|
||||
import llnl.util.tty.color as color
|
||||
from llnl.util.tty.colify import colify
|
||||
|
||||
import spack.builder
|
||||
import spack.deptypes as dt
|
||||
import spack.fetch_strategy as fs
|
||||
import spack.install_test
|
||||
@@ -202,11 +203,13 @@ def print_namespace(pkg, args):
|
||||
def print_phases(pkg, args):
|
||||
"""output installation phases"""
|
||||
|
||||
if hasattr(pkg.builder, "phases") and pkg.builder.phases:
|
||||
builder = spack.builder.create(pkg)
|
||||
|
||||
if hasattr(builder, "phases") and builder.phases:
|
||||
color.cprint("")
|
||||
color.cprint(section_title("Installation Phases:"))
|
||||
phase_str = ""
|
||||
for phase in pkg.builder.phases:
|
||||
for phase in builder.phases:
|
||||
phase_str += " {0}".format(phase)
|
||||
color.cprint(phase_str)
|
||||
|
||||
|
@@ -34,12 +34,13 @@ class OpenMpi(Package):
|
||||
import collections.abc
|
||||
import os.path
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, Union
|
||||
from typing import Any, Callable, List, Optional, Tuple, Union
|
||||
|
||||
import llnl.util.lang
|
||||
import llnl.util.tty.color
|
||||
|
||||
import spack.deptypes as dt
|
||||
import spack.package_base
|
||||
import spack.patch
|
||||
import spack.spec
|
||||
import spack.util.crypto
|
||||
@@ -56,13 +57,8 @@ class OpenMpi(Package):
|
||||
VersionLookupError,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import spack.package_base
|
||||
|
||||
__all__ = [
|
||||
"DirectiveError",
|
||||
"DirectiveMeta",
|
||||
"DisableRedistribute",
|
||||
"version",
|
||||
"conditional",
|
||||
"conflicts",
|
||||
@@ -85,15 +81,15 @@ class OpenMpi(Package):
|
||||
|
||||
SpecType = str
|
||||
DepType = Union[Tuple[str, ...], str]
|
||||
WhenType = Optional[Union["spack.spec.Spec", str, bool]]
|
||||
Patcher = Callable[[Union["spack.package_base.PackageBase", Dependency]], None]
|
||||
WhenType = Optional[Union[spack.spec.Spec, str, bool]]
|
||||
Patcher = Callable[[Union[spack.package_base.PackageBase, Dependency]], None]
|
||||
PatchesType = Optional[Union[Patcher, str, List[Union[Patcher, str]]]]
|
||||
|
||||
|
||||
SUPPORTED_LANGUAGES = ("fortran", "cxx", "c")
|
||||
|
||||
|
||||
def _make_when_spec(value: WhenType) -> Optional["spack.spec.Spec"]:
|
||||
def _make_when_spec(value: WhenType) -> Optional[spack.spec.Spec]:
|
||||
"""Create a ``Spec`` that indicates when a directive should be applied.
|
||||
|
||||
Directives with ``when`` specs, e.g.:
|
||||
@@ -138,7 +134,7 @@ def _make_when_spec(value: WhenType) -> Optional["spack.spec.Spec"]:
|
||||
return spack.spec.Spec(value)
|
||||
|
||||
|
||||
SubmoduleCallback = Callable[["spack.package_base.PackageBase"], Union[str, List[str], bool]]
|
||||
SubmoduleCallback = Callable[[spack.package_base.PackageBase], Union[str, List[str], bool]]
|
||||
directive = DirectiveMeta.directive
|
||||
|
||||
|
||||
@@ -254,8 +250,8 @@ def _execute_version(pkg, ver, **kwargs):
|
||||
|
||||
|
||||
def _depends_on(
|
||||
pkg: "spack.package_base.PackageBase",
|
||||
spec: "spack.spec.Spec",
|
||||
pkg: spack.package_base.PackageBase,
|
||||
spec: spack.spec.Spec,
|
||||
*,
|
||||
when: WhenType = None,
|
||||
type: DepType = dt.DEFAULT_TYPES,
|
||||
@@ -334,7 +330,7 @@ def conflicts(conflict_spec: SpecType, when: WhenType = None, msg: Optional[str]
|
||||
msg (str): optional user defined message
|
||||
"""
|
||||
|
||||
def _execute_conflicts(pkg: "spack.package_base.PackageBase"):
|
||||
def _execute_conflicts(pkg: spack.package_base.PackageBase):
|
||||
# If when is not specified the conflict always holds
|
||||
when_spec = _make_when_spec(when)
|
||||
if not when_spec:
|
||||
@@ -375,19 +371,12 @@ def depends_on(
|
||||
assert type == "build", "languages must be of 'build' type"
|
||||
return _language(lang_spec_str=spec, when=when)
|
||||
|
||||
def _execute_depends_on(pkg: "spack.package_base.PackageBase"):
|
||||
def _execute_depends_on(pkg: spack.package_base.PackageBase):
|
||||
_depends_on(pkg, dep_spec, when=when, type=type, patches=patches)
|
||||
|
||||
return _execute_depends_on
|
||||
|
||||
|
||||
#: Store whether a given Spec source/binary should not be redistributed.
|
||||
class DisableRedistribute:
|
||||
def __init__(self, source, binary):
|
||||
self.source = source
|
||||
self.binary = binary
|
||||
|
||||
|
||||
@directive("disable_redistribute")
|
||||
def redistribute(source=None, binary=None, when: WhenType = None):
|
||||
"""Can be used inside a Package definition to declare that
|
||||
@@ -404,7 +393,7 @@ def redistribute(source=None, binary=None, when: WhenType = None):
|
||||
|
||||
|
||||
def _execute_redistribute(
|
||||
pkg: "spack.package_base.PackageBase", source=None, binary=None, when: WhenType = None
|
||||
pkg: spack.package_base.PackageBase, source=None, binary=None, when: WhenType = None
|
||||
):
|
||||
if source is None and binary is None:
|
||||
return
|
||||
@@ -434,7 +423,7 @@ def _execute_redistribute(
|
||||
if not binary:
|
||||
disable.binary = True
|
||||
else:
|
||||
pkg.disable_redistribute[when_spec] = DisableRedistribute(
|
||||
pkg.disable_redistribute[when_spec] = spack.package_base.DisableRedistribute(
|
||||
source=not source, binary=not binary
|
||||
)
|
||||
|
||||
@@ -480,7 +469,7 @@ def provides(*specs: SpecType, when: WhenType = None):
|
||||
when: condition when this provides clause needs to be considered
|
||||
"""
|
||||
|
||||
def _execute_provides(pkg: "spack.package_base.PackageBase"):
|
||||
def _execute_provides(pkg: spack.package_base.PackageBase):
|
||||
import spack.parser # Avoid circular dependency
|
||||
|
||||
when_spec = _make_when_spec(when)
|
||||
@@ -528,7 +517,7 @@ def can_splice(
|
||||
variants will be skipped by '*'.
|
||||
"""
|
||||
|
||||
def _execute_can_splice(pkg: "spack.package_base.PackageBase"):
|
||||
def _execute_can_splice(pkg: spack.package_base.PackageBase):
|
||||
when_spec = _make_when_spec(when)
|
||||
if isinstance(match_variants, str) and match_variants != "*":
|
||||
raise ValueError(
|
||||
@@ -569,7 +558,7 @@ def patch(
|
||||
compressed URL patches)
|
||||
"""
|
||||
|
||||
def _execute_patch(pkg_or_dep: Union["spack.package_base.PackageBase", Dependency]):
|
||||
def _execute_patch(pkg_or_dep: Union[spack.package_base.PackageBase, Dependency]):
|
||||
pkg = pkg_or_dep
|
||||
if isinstance(pkg, Dependency):
|
||||
pkg = pkg.pkg
|
||||
@@ -893,7 +882,7 @@ def requires(*requirement_specs: str, policy="one_of", when=None, msg=None):
|
||||
msg: optional user defined message
|
||||
"""
|
||||
|
||||
def _execute_requires(pkg: "spack.package_base.PackageBase"):
|
||||
def _execute_requires(pkg: spack.package_base.PackageBase):
|
||||
if policy not in ("one_of", "any_of"):
|
||||
err_msg = (
|
||||
f"the 'policy' argument of the 'requires' directive in {pkg.name} is set "
|
||||
@@ -918,7 +907,7 @@ def _execute_requires(pkg: "spack.package_base.PackageBase"):
|
||||
def _language(lang_spec_str: str, *, when: Optional[Union[str, bool]] = None):
|
||||
"""Temporary implementation of language virtuals, until compilers are proper dependencies."""
|
||||
|
||||
def _execute_languages(pkg: "spack.package_base.PackageBase"):
|
||||
def _execute_languages(pkg: spack.package_base.PackageBase):
|
||||
when_spec = _make_when_spec(when)
|
||||
if not when_spec:
|
||||
return
|
||||
|
@@ -23,7 +23,6 @@
|
||||
from llnl.util.tty.color import colorize
|
||||
|
||||
import spack.build_environment
|
||||
import spack.builder
|
||||
import spack.config
|
||||
import spack.error
|
||||
import spack.package_base
|
||||
@@ -353,9 +352,7 @@ def status(self, name: str, status: "TestStatus", msg: Optional[str] = None):
|
||||
self.test_parts[part_name] = status
|
||||
self.counts[status] += 1
|
||||
|
||||
def phase_tests(
|
||||
self, builder: spack.builder.Builder, phase_name: str, method_names: List[str]
|
||||
):
|
||||
def phase_tests(self, builder, phase_name: str, method_names: List[str]):
|
||||
"""Execute the builder's package phase-time tests.
|
||||
|
||||
Args:
|
||||
|
@@ -50,6 +50,7 @@
|
||||
|
||||
import spack.binary_distribution as binary_distribution
|
||||
import spack.build_environment
|
||||
import spack.builder
|
||||
import spack.config
|
||||
import spack.database
|
||||
import spack.deptypes as dt
|
||||
@@ -212,7 +213,7 @@ def _check_last_phase(pkg: "spack.package_base.PackageBase") -> None:
|
||||
Raises:
|
||||
``BadInstallPhase`` if stop_before or last phase is invalid
|
||||
"""
|
||||
phases = pkg.builder.phases # type: ignore[attr-defined]
|
||||
phases = spack.builder.create(pkg).phases # type: ignore[attr-defined]
|
||||
if pkg.stop_before_phase and pkg.stop_before_phase not in phases: # type: ignore[attr-defined]
|
||||
raise BadInstallPhase(pkg.name, pkg.stop_before_phase) # type: ignore[attr-defined]
|
||||
|
||||
@@ -661,7 +662,7 @@ def log(pkg: "spack.package_base.PackageBase") -> None:
|
||||
spack.store.STORE.layout.metadata_path(pkg.spec), "archived-files"
|
||||
)
|
||||
|
||||
for glob_expr in pkg.builder.archive_files:
|
||||
for glob_expr in spack.builder.create(pkg).archive_files:
|
||||
# Check that we are trying to copy things that are
|
||||
# in the stage tree (not arbitrary files)
|
||||
abs_expr = os.path.realpath(glob_expr)
|
||||
@@ -2394,7 +2395,6 @@ def _install_source(self) -> None:
|
||||
fs.install_tree(pkg.stage.source_path, src_target)
|
||||
|
||||
def _real_install(self) -> None:
|
||||
import spack.builder
|
||||
|
||||
pkg = self.pkg
|
||||
|
||||
|
@@ -29,6 +29,7 @@
|
||||
import spack.config
|
||||
import spack.error
|
||||
import spack.fetch_strategy
|
||||
import spack.mirror
|
||||
import spack.oci.image
|
||||
import spack.repo
|
||||
import spack.spec
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
import llnl.util.filesystem
|
||||
|
||||
import spack.builder
|
||||
import spack.phase_callbacks
|
||||
|
||||
|
||||
def filter_compiler_wrappers(*files, **kwargs):
|
||||
@@ -111,4 +111,4 @@ def _filter_compiler_wrappers_impl(pkg_or_builder):
|
||||
if pkg.compiler.name == "nag":
|
||||
x.filter("-Wl,--enable-new-dtags", "", **filter_kwargs)
|
||||
|
||||
spack.builder.run_after(after)(_filter_compiler_wrappers_impl)
|
||||
spack.phase_callbacks.run_after(after)(_filter_compiler_wrappers_impl)
|
||||
|
@@ -74,7 +74,7 @@
|
||||
from spack.build_systems.sourceware import SourcewarePackage
|
||||
from spack.build_systems.waf import WafPackage
|
||||
from spack.build_systems.xorg import XorgPackage
|
||||
from spack.builder import run_after, run_before
|
||||
from spack.builder import BaseBuilder
|
||||
from spack.config import determine_number_of_jobs
|
||||
from spack.deptypes import ALL_TYPES as all_deptypes
|
||||
from spack.directives import *
|
||||
@@ -100,6 +100,7 @@
|
||||
on_package_attributes,
|
||||
)
|
||||
from spack.package_completions import *
|
||||
from spack.phase_callbacks import run_after, run_before
|
||||
from spack.spec import InvalidSpecDetected, Spec
|
||||
from spack.util.executable import *
|
||||
from spack.util.filesystem import file_command, fix_darwin_install_name, mime_type
|
||||
|
@@ -32,18 +32,18 @@
|
||||
from llnl.util.lang import classproperty, memoized
|
||||
from llnl.util.link_tree import LinkTree
|
||||
|
||||
import spack.builder
|
||||
import spack.compilers
|
||||
import spack.config
|
||||
import spack.dependency
|
||||
import spack.deptypes as dt
|
||||
import spack.directives
|
||||
import spack.directives_meta
|
||||
import spack.error
|
||||
import spack.fetch_strategy as fs
|
||||
import spack.hooks
|
||||
import spack.mirror
|
||||
import spack.multimethod
|
||||
import spack.patch
|
||||
import spack.phase_callbacks
|
||||
import spack.repo
|
||||
import spack.spec
|
||||
import spack.store
|
||||
@@ -51,9 +51,9 @@
|
||||
import spack.util.environment
|
||||
import spack.util.path
|
||||
import spack.util.web
|
||||
import spack.variant
|
||||
from spack.error import InstallError, NoURLError, PackageError
|
||||
from spack.filesystem_view import YamlFilesystemView
|
||||
from spack.install_test import PackageTest, TestSuite
|
||||
from spack.solver.version_order import concretization_version_order
|
||||
from spack.stage import DevelopStage, ResourceStage, Stage, StageComposite, compute_stage_name
|
||||
from spack.util.package_hash import package_hash
|
||||
@@ -299,9 +299,9 @@ def determine_variants(cls, objs, version_str):
|
||||
|
||||
|
||||
class PackageMeta(
|
||||
spack.builder.PhaseCallbacksMeta,
|
||||
spack.phase_callbacks.PhaseCallbacksMeta,
|
||||
DetectablePackageMeta,
|
||||
spack.directives.DirectiveMeta,
|
||||
spack.directives_meta.DirectiveMeta,
|
||||
spack.multimethod.MultiMethodMeta,
|
||||
):
|
||||
"""
|
||||
@@ -453,7 +453,7 @@ def _names(when_indexed_dictionary: WhenDict) -> List[str]:
|
||||
return sorted(all_names)
|
||||
|
||||
|
||||
WhenVariantList = List[Tuple["spack.spec.Spec", "spack.variant.Variant"]]
|
||||
WhenVariantList = List[Tuple[spack.spec.Spec, spack.variant.Variant]]
|
||||
|
||||
|
||||
def _remove_overridden_vdefs(variant_defs: WhenVariantList) -> None:
|
||||
@@ -492,41 +492,14 @@ class Hipblas:
|
||||
i += 1
|
||||
|
||||
|
||||
class RedistributionMixin:
|
||||
"""Logic for determining whether a Package is source/binary
|
||||
redistributable.
|
||||
"""
|
||||
|
||||
#: Store whether a given Spec source/binary should not be
|
||||
#: redistributed.
|
||||
disable_redistribute: Dict["spack.spec.Spec", "spack.directives.DisableRedistribute"]
|
||||
|
||||
# Source redistribution must be determined before concretization
|
||||
# (because source mirrors work with un-concretized Specs).
|
||||
@classmethod
|
||||
def redistribute_source(cls, spec):
|
||||
"""Whether it should be possible to add the source of this
|
||||
package to a Spack mirror.
|
||||
"""
|
||||
for when_spec, disable_redistribute in cls.disable_redistribute.items():
|
||||
if disable_redistribute.source and spec.satisfies(when_spec):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def redistribute_binary(self):
|
||||
"""Whether it should be possible to create a binary out of an
|
||||
installed instance of this package.
|
||||
"""
|
||||
for when_spec, disable_redistribute in self.__class__.disable_redistribute.items():
|
||||
if disable_redistribute.binary and self.spec.satisfies(when_spec):
|
||||
return False
|
||||
|
||||
return True
|
||||
#: Store whether a given Spec source/binary should not be redistributed.
|
||||
class DisableRedistribute:
|
||||
def __init__(self, source, binary):
|
||||
self.source = source
|
||||
self.binary = binary
|
||||
|
||||
|
||||
class PackageBase(WindowsRPath, PackageViewMixin, RedistributionMixin, metaclass=PackageMeta):
|
||||
class PackageBase(WindowsRPath, PackageViewMixin, metaclass=PackageMeta):
|
||||
"""This is the superclass for all spack packages.
|
||||
|
||||
***The Package class***
|
||||
@@ -612,17 +585,20 @@ class PackageBase(WindowsRPath, PackageViewMixin, RedistributionMixin, metaclass
|
||||
# Declare versions dictionary as placeholder for values.
|
||||
# This allows analysis tools to correctly interpret the class attributes.
|
||||
versions: dict
|
||||
dependencies: Dict["spack.spec.Spec", Dict[str, "spack.dependency.Dependency"]]
|
||||
conflicts: Dict["spack.spec.Spec", List[Tuple["spack.spec.Spec", Optional[str]]]]
|
||||
dependencies: Dict[spack.spec.Spec, Dict[str, spack.dependency.Dependency]]
|
||||
conflicts: Dict[spack.spec.Spec, List[Tuple[spack.spec.Spec, Optional[str]]]]
|
||||
requirements: Dict[
|
||||
"spack.spec.Spec", List[Tuple[Tuple["spack.spec.Spec", ...], str, Optional[str]]]
|
||||
spack.spec.Spec, List[Tuple[Tuple[spack.spec.Spec, ...], str, Optional[str]]]
|
||||
]
|
||||
provided: Dict["spack.spec.Spec", Set["spack.spec.Spec"]]
|
||||
provided_together: Dict["spack.spec.Spec", List[Set[str]]]
|
||||
patches: Dict["spack.spec.Spec", List["spack.patch.Patch"]]
|
||||
variants: Dict["spack.spec.Spec", Dict[str, "spack.variant.Variant"]]
|
||||
languages: Dict["spack.spec.Spec", Set[str]]
|
||||
splice_specs: Dict["spack.spec.Spec", Tuple["spack.spec.Spec", Union[None, str, List[str]]]]
|
||||
provided: Dict[spack.spec.Spec, Set[spack.spec.Spec]]
|
||||
provided_together: Dict[spack.spec.Spec, List[Set[str]]]
|
||||
patches: Dict[spack.spec.Spec, List[spack.patch.Patch]]
|
||||
variants: Dict[spack.spec.Spec, Dict[str, spack.variant.Variant]]
|
||||
languages: Dict[spack.spec.Spec, Set[str]]
|
||||
splice_specs: Dict[spack.spec.Spec, Tuple[spack.spec.Spec, Union[None, str, List[str]]]]
|
||||
|
||||
#: Store whether a given Spec source/binary should not be redistributed.
|
||||
disable_redistribute: Dict[spack.spec.Spec, DisableRedistribute]
|
||||
|
||||
#: By default, packages are not virtual
|
||||
#: Virtual packages override this attribute
|
||||
@@ -737,11 +713,11 @@ class PackageBase(WindowsRPath, PackageViewMixin, RedistributionMixin, metaclass
|
||||
test_requires_compiler: bool = False
|
||||
|
||||
#: TestSuite instance used to manage stand-alone tests for 1+ specs.
|
||||
test_suite: Optional["TestSuite"] = None
|
||||
test_suite: Optional[Any] = None
|
||||
|
||||
def __init__(self, spec):
|
||||
# this determines how the package should be built.
|
||||
self.spec: "spack.spec.Spec" = spec
|
||||
self.spec: spack.spec.Spec = spec
|
||||
|
||||
# Allow custom staging paths for packages
|
||||
self.path = None
|
||||
@@ -759,7 +735,7 @@ def __init__(self, spec):
|
||||
# init internal variables
|
||||
self._stage: Optional[StageComposite] = None
|
||||
self._fetcher = None
|
||||
self._tester: Optional["PackageTest"] = None
|
||||
self._tester: Optional[Any] = None
|
||||
|
||||
# Set up timing variables
|
||||
self._fetch_time = 0.0
|
||||
@@ -809,9 +785,7 @@ def variant_definitions(cls, name: str) -> WhenVariantList:
|
||||
return defs
|
||||
|
||||
@classmethod
|
||||
def variant_items(
|
||||
cls,
|
||||
) -> Iterable[Tuple["spack.spec.Spec", Dict[str, "spack.variant.Variant"]]]:
|
||||
def variant_items(cls) -> Iterable[Tuple[spack.spec.Spec, Dict[str, spack.variant.Variant]]]:
|
||||
"""Iterate over ``cls.variants.items()`` with overridden definitions removed."""
|
||||
# Note: This is quadratic in the average number of variant definitions per name.
|
||||
# That is likely close to linear in practice, as there are few variants with
|
||||
@@ -829,7 +803,7 @@ def variant_items(
|
||||
if filtered_variants_by_name:
|
||||
yield when, filtered_variants_by_name
|
||||
|
||||
def get_variant(self, name: str) -> "spack.variant.Variant":
|
||||
def get_variant(self, name: str) -> spack.variant.Variant:
|
||||
"""Get the highest precedence variant definition matching this package's spec.
|
||||
|
||||
Arguments:
|
||||
@@ -1004,6 +978,26 @@ def global_license_file(self):
|
||||
self.global_license_dir, self.name, os.path.basename(self.license_files[0])
|
||||
)
|
||||
|
||||
# Source redistribution must be determined before concretization (because source mirrors work
|
||||
# with abstract specs).
|
||||
@classmethod
|
||||
def redistribute_source(cls, spec):
|
||||
"""Whether it should be possible to add the source of this
|
||||
package to a Spack mirror."""
|
||||
for when_spec, disable_redistribute in cls.disable_redistribute.items():
|
||||
if disable_redistribute.source and spec.satisfies(when_spec):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def redistribute_binary(self):
|
||||
"""Whether it should be possible to create a binary out of an installed instance of this
|
||||
package."""
|
||||
for when_spec, disable_redistribute in self.disable_redistribute.items():
|
||||
if disable_redistribute.binary and self.spec.satisfies(when_spec):
|
||||
return False
|
||||
return True
|
||||
|
||||
# NOTE: return type should be Optional[Literal['all', 'specific', 'none']] in
|
||||
# Python 3.8+, but we still support 3.6.
|
||||
@property
|
||||
@@ -1353,11 +1347,13 @@ def archive_install_test_log(self):
|
||||
|
||||
@property
|
||||
def tester(self):
|
||||
import spack.install_test
|
||||
|
||||
if not self.spec.versions.concrete:
|
||||
raise ValueError("Cannot retrieve tester for package without concrete version.")
|
||||
|
||||
if not self._tester:
|
||||
self._tester = PackageTest(self)
|
||||
self._tester = spack.install_test.PackageTest(self)
|
||||
return self._tester
|
||||
|
||||
@property
|
||||
@@ -2014,72 +2010,58 @@ def build_system_flags(
|
||||
"""
|
||||
return None, None, flags
|
||||
|
||||
def setup_run_environment(self, env):
|
||||
def setup_run_environment(self, env: spack.util.environment.EnvironmentModifications) -> None:
|
||||
"""Sets up the run environment for a package.
|
||||
|
||||
Args:
|
||||
env (spack.util.environment.EnvironmentModifications): environment
|
||||
modifications to be applied when the package is run. Package authors
|
||||
env: environment modifications to be applied when the package is run. Package authors
|
||||
can call methods on it to alter the run environment.
|
||||
"""
|
||||
pass
|
||||
|
||||
def setup_dependent_run_environment(self, env, dependent_spec):
|
||||
def setup_dependent_run_environment(
|
||||
self, env: spack.util.environment.EnvironmentModifications, dependent_spec: spack.spec.Spec
|
||||
) -> None:
|
||||
"""Sets up the run environment of packages that depend on this one.
|
||||
|
||||
This is similar to ``setup_run_environment``, but it is used to
|
||||
modify the run environments of packages that *depend* on this one.
|
||||
This is similar to ``setup_run_environment``, but it is used to modify the run environment
|
||||
of a package that *depends* on this one.
|
||||
|
||||
This gives packages like Python and others that follow the extension
|
||||
model a way to implement common environment or run-time settings
|
||||
for dependencies.
|
||||
This gives packages like Python and others that follow the extension model a way to
|
||||
implement common environment or run-time settings for dependencies.
|
||||
|
||||
Args:
|
||||
env (spack.util.environment.EnvironmentModifications): environment
|
||||
modifications to be applied when the dependent package is run.
|
||||
Package authors can call methods on it to alter the build environment.
|
||||
env: environment modifications to be applied when the dependent package is run. Package
|
||||
authors can call methods on it to alter the build environment.
|
||||
|
||||
dependent_spec (spack.spec.Spec): The spec of the dependent package
|
||||
about to be run. This allows the extendee (self) to query
|
||||
the dependent's state. Note that *this* package's spec is
|
||||
dependent_spec: The spec of the dependent package about to be run. This allows the
|
||||
extendee (self) to query the dependent's state. Note that *this* package's spec is
|
||||
available as ``self.spec``
|
||||
"""
|
||||
pass
|
||||
|
||||
def setup_dependent_package(self, module, dependent_spec):
|
||||
"""Set up Python module-scope variables for dependent packages.
|
||||
def setup_dependent_package(self, module, dependent_spec: spack.spec.Spec) -> None:
|
||||
"""Set up module-scope global variables for dependent packages.
|
||||
|
||||
Called before the install() method of dependents.
|
||||
|
||||
Default implementation does nothing, but this can be
|
||||
overridden by an extendable package to set up the module of
|
||||
its extensions. This is useful if there are some common steps
|
||||
to installing all extensions for a certain package.
|
||||
This function is called when setting up the build and run environments of a DAG.
|
||||
|
||||
Examples:
|
||||
|
||||
1. Extensions often need to invoke the ``python`` interpreter
|
||||
from the Python installation being extended. This routine
|
||||
can put a ``python()`` Executable object in the module scope
|
||||
for the extension package to simplify extension installs.
|
||||
1. Extensions often need to invoke the ``python`` interpreter from the Python installation
|
||||
being extended. This routine can put a ``python`` Executable as a global in the module
|
||||
scope for the extension package to simplify extension installs.
|
||||
|
||||
2. MPI compilers could set some variables in the dependent's
|
||||
scope that point to ``mpicc``, ``mpicxx``, etc., allowing
|
||||
them to be called by common name regardless of which MPI is used.
|
||||
|
||||
3. BLAS/LAPACK implementations can set some variables
|
||||
indicating the path to their libraries, since these
|
||||
paths differ by BLAS/LAPACK implementation.
|
||||
2. MPI compilers could set some variables in the dependent's scope that point to ``mpicc``,
|
||||
``mpicxx``, etc., allowing them to be called by common name regardless of which MPI is
|
||||
used.
|
||||
|
||||
Args:
|
||||
module (spack.package_base.PackageBase.module): The Python ``module``
|
||||
object of the dependent package. Packages can use this to set
|
||||
module-scope variables for the dependent to use.
|
||||
module: The Python ``module`` object of the dependent package. Packages can use this to
|
||||
set module-scope variables for the dependent to use.
|
||||
|
||||
dependent_spec (spack.spec.Spec): The spec of the dependent package
|
||||
about to be built. This allows the extendee (self) to
|
||||
query the dependent's state. Note that *this*
|
||||
package's spec is available as ``self.spec``.
|
||||
dependent_spec: The spec of the dependent package about to be built. This allows the
|
||||
extendee (self) to query the dependent's state. Note that *this* package's spec is
|
||||
available as ``self.spec``.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -2106,7 +2088,7 @@ def flag_handler(self, var: FLAG_HANDLER_TYPE) -> None:
|
||||
# arguments. This is implemented for build system classes where
|
||||
# appropriate and will otherwise raise a NotImplementedError.
|
||||
|
||||
def flags_to_build_system_args(self, flags):
|
||||
def flags_to_build_system_args(self, flags: Dict[str, List[str]]) -> None:
|
||||
# Takes flags as a dict name: list of values
|
||||
if any(v for v in flags.values()):
|
||||
msg = "The {0} build system".format(self.__class__.__name__)
|
||||
@@ -2309,10 +2291,6 @@ def rpath_args(self):
|
||||
"""
|
||||
return " ".join("-Wl,-rpath,%s" % p for p in self.rpath)
|
||||
|
||||
@property
|
||||
def builder(self):
|
||||
return spack.builder.create(self)
|
||||
|
||||
|
||||
inject_flags = PackageBase.inject_flags
|
||||
env_flags = PackageBase.env_flags
|
||||
|
105
lib/spack/spack/phase_callbacks.py
Normal file
105
lib/spack/spack/phase_callbacks.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
import collections
|
||||
|
||||
import llnl.util.lang as lang
|
||||
|
||||
#: An object of this kind is a shared global state used to collect callbacks during
|
||||
#: class definition time, and is flushed when the class object is created at the end
|
||||
#: of the class definition
|
||||
#:
|
||||
#: Args:
|
||||
#: attribute_name (str): name of the attribute that will be attached to the builder
|
||||
#: callbacks (list): container used to temporarily aggregate the callbacks
|
||||
CallbackTemporaryStage = collections.namedtuple(
|
||||
"CallbackTemporaryStage", ["attribute_name", "callbacks"]
|
||||
)
|
||||
|
||||
#: Shared global state to aggregate "@run_before" callbacks
|
||||
_RUN_BEFORE = CallbackTemporaryStage(attribute_name="run_before_callbacks", callbacks=[])
|
||||
#: Shared global state to aggregate "@run_after" callbacks
|
||||
_RUN_AFTER = CallbackTemporaryStage(attribute_name="run_after_callbacks", callbacks=[])
|
||||
|
||||
|
||||
class PhaseCallbacksMeta(type):
|
||||
"""Permit to register arbitrary functions during class definition and run them
|
||||
later, before or after a given install phase.
|
||||
|
||||
Each method decorated with ``run_before`` or ``run_after`` gets temporarily
|
||||
stored in a global shared state when a class being defined is parsed by the Python
|
||||
interpreter. At class definition time that temporary storage gets flushed and a list
|
||||
of callbacks is attached to the class being defined.
|
||||
"""
|
||||
|
||||
def __new__(mcs, name, bases, attr_dict):
|
||||
for temporary_stage in (_RUN_BEFORE, _RUN_AFTER):
|
||||
staged_callbacks = temporary_stage.callbacks
|
||||
|
||||
# Here we have an adapter from an old-style package. This means there is no
|
||||
# hierarchy of builders, and every callback that had to be combined between
|
||||
# *Package and *Builder has been combined already by _PackageAdapterMeta
|
||||
if name == "Adapter":
|
||||
continue
|
||||
|
||||
# If we are here we have callbacks. To get a complete list, we accumulate all the
|
||||
# callbacks from base classes, we deduplicate them, then prepend what we have
|
||||
# registered here.
|
||||
#
|
||||
# The order should be:
|
||||
# 1. Callbacks are registered in order within the same class
|
||||
# 2. Callbacks defined in derived classes precede those defined in base
|
||||
# classes
|
||||
callbacks_from_base = []
|
||||
for base in bases:
|
||||
current_callbacks = getattr(base, temporary_stage.attribute_name, None)
|
||||
if not current_callbacks:
|
||||
continue
|
||||
callbacks_from_base.extend(current_callbacks)
|
||||
callbacks_from_base = list(lang.dedupe(callbacks_from_base))
|
||||
# Set the callbacks in this class and flush the temporary stage
|
||||
attr_dict[temporary_stage.attribute_name] = staged_callbacks[:] + callbacks_from_base
|
||||
del temporary_stage.callbacks[:]
|
||||
|
||||
return super(PhaseCallbacksMeta, mcs).__new__(mcs, name, bases, attr_dict)
|
||||
|
||||
@staticmethod
|
||||
def run_after(phase, when=None):
|
||||
"""Decorator to register a function for running after a given phase.
|
||||
|
||||
Args:
|
||||
phase (str): phase after which the function must run.
|
||||
when (str): condition under which the function is run (if None, it is always run).
|
||||
"""
|
||||
|
||||
def _decorator(fn):
|
||||
key = (phase, when)
|
||||
item = (key, fn)
|
||||
_RUN_AFTER.callbacks.append(item)
|
||||
return fn
|
||||
|
||||
return _decorator
|
||||
|
||||
@staticmethod
|
||||
def run_before(phase, when=None):
|
||||
"""Decorator to register a function for running before a given phase.
|
||||
|
||||
Args:
|
||||
phase (str): phase before which the function must run.
|
||||
when (str): condition under which the function is run (if None, it is always run).
|
||||
"""
|
||||
|
||||
def _decorator(fn):
|
||||
key = (phase, when)
|
||||
item = (key, fn)
|
||||
_RUN_BEFORE.callbacks.append(item)
|
||||
return fn
|
||||
|
||||
return _decorator
|
||||
|
||||
|
||||
# Export these names as standalone to be used in packages
|
||||
run_after = PhaseCallbacksMeta.run_after
|
||||
run_before = PhaseCallbacksMeta.run_before
|
@@ -15,6 +15,7 @@
|
||||
|
||||
import spack.build_systems.autotools
|
||||
import spack.build_systems.cmake
|
||||
import spack.builder
|
||||
import spack.environment
|
||||
import spack.error
|
||||
import spack.paths
|
||||
@@ -149,7 +150,7 @@ def test_libtool_archive_files_are_deleted_by_default(self, mutable_database):
|
||||
|
||||
# Assert the libtool archive is not there and we have
|
||||
# a log of removed files
|
||||
assert not os.path.exists(s.package.builder.libtool_archive_file)
|
||||
assert not os.path.exists(spack.builder.create(s.package).libtool_archive_file)
|
||||
search_directory = os.path.join(s.prefix, ".spack")
|
||||
libtool_deletion_log = fs.find(search_directory, "removed_la_files.txt", recursive=True)
|
||||
assert libtool_deletion_log
|
||||
@@ -160,11 +161,13 @@ def test_libtool_archive_files_might_be_installed_on_demand(
|
||||
# Install a package that creates a mock libtool archive,
|
||||
# patch its package to preserve the installation
|
||||
s = Spec("libtool-deletion").concretized()
|
||||
monkeypatch.setattr(type(s.package.builder), "install_libtool_archives", True)
|
||||
monkeypatch.setattr(
|
||||
type(spack.builder.create(s.package)), "install_libtool_archives", True
|
||||
)
|
||||
PackageInstaller([s.package], explicit=True).install()
|
||||
|
||||
# Assert libtool archives are installed
|
||||
assert os.path.exists(s.package.builder.libtool_archive_file)
|
||||
assert os.path.exists(spack.builder.create(s.package).libtool_archive_file)
|
||||
|
||||
def test_autotools_gnuconfig_replacement(self, mutable_database):
|
||||
"""
|
||||
@@ -261,7 +264,7 @@ def test_cmake_std_args(self, default_mock_concretization):
|
||||
# Call the function on a CMakePackage instance
|
||||
s = default_mock_concretization("cmake-client")
|
||||
expected = spack.build_systems.cmake.CMakeBuilder.std_args(s.package)
|
||||
assert s.package.builder.std_cmake_args == expected
|
||||
assert spack.builder.create(s.package).std_cmake_args == expected
|
||||
|
||||
# Call it on another kind of package
|
||||
s = default_mock_concretization("mpich")
|
||||
@@ -381,7 +384,9 @@ def test_autotools_args_from_conditional_variant(default_mock_concretization):
|
||||
is not met. When this is the case, the variant is not set in the spec."""
|
||||
s = default_mock_concretization("autotools-conditional-variants-test")
|
||||
assert "example" not in s.variants
|
||||
assert len(s.package.builder._activate_or_not("example", "enable", "disable")) == 0
|
||||
assert (
|
||||
len(spack.builder.create(s.package)._activate_or_not("example", "enable", "disable")) == 0
|
||||
)
|
||||
|
||||
|
||||
def test_autoreconf_search_path_args_multiple(default_mock_concretization, tmpdir):
|
||||
|
@@ -38,7 +38,7 @@
|
||||
import spack.compiler
|
||||
import spack.compilers
|
||||
import spack.config
|
||||
import spack.directives
|
||||
import spack.directives_meta
|
||||
import spack.environment as ev
|
||||
import spack.error
|
||||
import spack.modules.common
|
||||
@@ -1754,7 +1754,7 @@ def clear_directive_functions():
|
||||
# Make sure any directive functions overidden by tests are cleared before
|
||||
# proceeding with subsequent tests that may depend on the original
|
||||
# functions.
|
||||
spack.directives.DirectiveMeta._directives_to_be_executed = []
|
||||
spack.directives_meta.DirectiveMeta._directives_to_be_executed = []
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@@ -2,7 +2,7 @@
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
import spack.builder
|
||||
import spack.phase_callbacks
|
||||
from spack.build_systems import generic
|
||||
from spack.package import *
|
||||
|
||||
@@ -17,7 +17,7 @@ class BuilderAndMixins(Package):
|
||||
version("1.0", md5="0123456789abcdef0123456789abcdef")
|
||||
|
||||
|
||||
class BuilderMixin(metaclass=spack.builder.PhaseCallbacksMeta):
|
||||
class BuilderMixin(metaclass=spack.phase_callbacks.PhaseCallbacksMeta):
|
||||
@run_before("install")
|
||||
def before_install(self):
|
||||
pass
|
||||
|
@@ -4,7 +4,7 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
import spack.pkg.builtin.mock.python as mp
|
||||
from spack.build_systems._checks import BaseBuilder, execute_install_time_tests
|
||||
from spack.build_systems._checks import BuilderWithDefaults, execute_install_time_tests
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ def test_callback(self):
|
||||
|
||||
|
||||
@spack.builder.builder("testcallback")
|
||||
class MyBuilder(BaseBuilder):
|
||||
class MyBuilder(BuilderWithDefaults):
|
||||
phases = ("install",)
|
||||
|
||||
#: Callback names for install-time test
|
||||
@@ -40,7 +40,7 @@ class MyBuilder(BaseBuilder):
|
||||
def install(self, pkg, spec, prefix):
|
||||
pkg.install(spec, prefix)
|
||||
|
||||
spack.builder.run_after("install")(execute_install_time_tests)
|
||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||
|
||||
def test_callback(self):
|
||||
self.pkg.test_callback()
|
||||
|
@@ -6,6 +6,7 @@
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from spack.build_systems.cmake import CMakeBuilder
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@@ -313,11 +314,11 @@ def cmake_args(self):
|
||||
|
||||
# hip support
|
||||
if spec.satisfies("+cuda"):
|
||||
args.append(self.builder.define_cuda_architectures(self))
|
||||
args.append(CMakeBuilder.define_cuda_architectures(self))
|
||||
|
||||
# hip support
|
||||
if spec.satisfies("+rocm"):
|
||||
args.append(self.builder.define_hip_architectures(self))
|
||||
args.append(CMakeBuilder.define_hip_architectures(self))
|
||||
|
||||
return args
|
||||
|
||||
|
@@ -70,7 +70,7 @@ def flag_handler(self, name, flags):
|
||||
return (None, None, flags)
|
||||
|
||||
def check(self):
|
||||
unit = Executable(join_path(self.builder.build_directory, "bin", "unit"))
|
||||
unit = Executable(join_path(self.build_directory, "bin", "unit"))
|
||||
skipped_tests = [
|
||||
"AssimpAPITest_aiMatrix3x3.aiMatrix3FromToTest",
|
||||
"AssimpAPITest_aiMatrix4x4.aiMatrix4FromToTest",
|
||||
|
@@ -39,5 +39,5 @@ def patch(self):
|
||||
filter_file(".+", "2.4.1", "VERSION")
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -117,8 +117,8 @@ def cmake_args(self):
|
||||
|
||||
def check(self):
|
||||
# If the tester fails to build, ensure that the check() fails.
|
||||
if os.path.isfile(join_path(self.builder.build_directory, "test", "tester")):
|
||||
with working_dir(self.builder.build_directory):
|
||||
if os.path.isfile(join_path(self.build_directory, "test", "tester")):
|
||||
with working_dir(self.build_directory):
|
||||
make("check")
|
||||
else:
|
||||
raise Exception("The tester was not built!")
|
||||
|
@@ -823,18 +823,6 @@ def is_64bit():
|
||||
def setup_run_environment(self, env):
|
||||
env.set("BOOST_ROOT", self.prefix)
|
||||
|
||||
def setup_dependent_package(self, module, dependent_spec):
|
||||
# Disable find package's config mode for versions of Boost that
|
||||
# didn't provide it. See https://github.com/spack/spack/issues/20169
|
||||
# and https://cmake.org/cmake/help/latest/module/FindBoost.html
|
||||
if self.spec.satisfies("boost@:1.69.0") and dependent_spec.satisfies("build_system=cmake"):
|
||||
args_fn = type(dependent_spec.package.builder).cmake_args
|
||||
|
||||
def _cmake_args(self):
|
||||
return ["-DBoost_NO_BOOST_CMAKE=ON"] + args_fn(self)
|
||||
|
||||
type(dependent_spec.package.builder).cmake_args = _cmake_args
|
||||
|
||||
def setup_dependent_build_environment(self, env, dependent_spec):
|
||||
if "+context" in self.spec and "context-impl" in self.spec.variants:
|
||||
context_impl = self.spec.variants["context-impl"].value
|
||||
|
@@ -129,5 +129,5 @@ def setup_run_environment(self, env):
|
||||
self._setup_bufr_environment(env, suffix)
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -98,5 +98,5 @@ def setup_run_environment(self, env):
|
||||
env.set("G2_INC" + suffix, join_path(self.prefix, "include_" + suffix))
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -94,5 +94,5 @@ def setup_run_environment(self, env):
|
||||
env.set("G2C_INC", join_path(self.prefix, "include"))
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -38,5 +38,5 @@ def cmake_args(self):
|
||||
return args
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -46,5 +46,5 @@ def flag_handler(self, name, flags):
|
||||
return (None, None, flags)
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -208,7 +208,7 @@ def libs(self):
|
||||
return find_libraries(["libglib*"], root=self.prefix, recursive=True)
|
||||
|
||||
|
||||
class BaseBuilder(metaclass=spack.builder.PhaseCallbacksMeta):
|
||||
class AnyBuilder(BaseBuilder):
|
||||
@property
|
||||
def dtrace_copy_path(self):
|
||||
return join_path(self.stage.source_path, "dtrace-copy")
|
||||
@@ -288,7 +288,7 @@ def gettext_libdir(self):
|
||||
filter_file(pattern, repl, myfile, backup=False)
|
||||
|
||||
|
||||
class MesonBuilder(BaseBuilder, spack.build_systems.meson.MesonBuilder):
|
||||
class MesonBuilder(AnyBuilder, spack.build_systems.meson.MesonBuilder):
|
||||
def meson_args(self):
|
||||
args = []
|
||||
if self.spec.satisfies("@2.63.5:"):
|
||||
@@ -330,7 +330,7 @@ def meson_args(self):
|
||||
return args
|
||||
|
||||
|
||||
class AutotoolsBuilder(BaseBuilder, spack.build_systems.autotools.AutotoolsBuilder):
|
||||
class AutotoolsBuilder(AnyBuilder, spack.build_systems.autotools.AutotoolsBuilder):
|
||||
def configure_args(self):
|
||||
args = []
|
||||
if self.spec.satisfies("+libmount"):
|
||||
|
@@ -56,5 +56,5 @@ def cmake_args(self):
|
||||
return args
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -145,7 +145,5 @@ def cmake_args(self):
|
||||
return args
|
||||
|
||||
def check(self):
|
||||
exe = Executable(
|
||||
join_path(self.builder.build_directory, "clients", "staging", "hipblas-test")
|
||||
)
|
||||
exe = Executable(join_path(self.build_directory, "clients", "staging", "hipblas-test"))
|
||||
exe("--gtest_filter=-*known_bug*")
|
||||
|
@@ -119,7 +119,7 @@ class Hipsolver(CMakePackage, CudaPackage, ROCmPackage):
|
||||
patch("0001-suite-sparse-include-path-6.1.1.patch", when="@6.1.1:")
|
||||
|
||||
def check(self):
|
||||
exe = join_path(self.builder.build_directory, "clients", "staging", "hipsolver-test")
|
||||
exe = join_path(self.build_directory, "clients", "staging", "hipsolver-test")
|
||||
exe = which(exe)
|
||||
exe(["--gtest_filter=-*known_bug*"])
|
||||
|
||||
|
@@ -121,5 +121,5 @@ def setup_run_environment(self, env):
|
||||
|
||||
@when("@4:")
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -48,5 +48,5 @@ def flag_handler(self, name, flags):
|
||||
return (None, None, flags)
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -129,8 +129,8 @@ def cmake_args(self):
|
||||
|
||||
def check(self):
|
||||
# If the tester fails to build, ensure that the check() fails.
|
||||
if os.path.isfile(join_path(self.builder.build_directory, "test", "tester")):
|
||||
with working_dir(self.builder.build_directory):
|
||||
if os.path.isfile(join_path(self.build_directory, "test", "tester")):
|
||||
with working_dir(self.build_directory):
|
||||
make("check")
|
||||
else:
|
||||
raise Exception("The tester was not built!")
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack.build_systems.cmake import CMakeBuilder
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@@ -45,6 +46,6 @@ def cmake_args(self):
|
||||
args = [self.define_from_variant("LC_BUILD_LIBPRESSIO_PLUGIN", "libpressio")]
|
||||
if self.spec.satisfies("+cuda"):
|
||||
args.append(self.define_from_variant("LC_BUILD_CUDA", "cuda"))
|
||||
args.append(self.builder.define_cuda_architectures(self))
|
||||
args.append(CMakeBuilder.define_cuda_architectures(self))
|
||||
|
||||
return args
|
||||
|
@@ -6,7 +6,6 @@
|
||||
|
||||
import llnl.util.filesystem as fs
|
||||
|
||||
import spack.builder
|
||||
from spack.build_systems import autotools, nmake
|
||||
from spack.package import *
|
||||
|
||||
@@ -225,7 +224,7 @@ def test_xmllint(self):
|
||||
xmllint("--dtdvalid", dtd_path, data_dir.join("info.xml"))
|
||||
|
||||
|
||||
class BaseBuilder(metaclass=spack.builder.PhaseCallbacksMeta):
|
||||
class AnyBuilder(BaseBuilder):
|
||||
@run_after("install")
|
||||
@on_package_attributes(run_tests=True)
|
||||
def import_module_test(self):
|
||||
@@ -234,7 +233,7 @@ def import_module_test(self):
|
||||
python("-c", "import libxml2")
|
||||
|
||||
|
||||
class AutotoolsBuilder(BaseBuilder, autotools.AutotoolsBuilder):
|
||||
class AutotoolsBuilder(AnyBuilder, autotools.AutotoolsBuilder):
|
||||
def configure_args(self):
|
||||
spec = self.spec
|
||||
|
||||
@@ -260,7 +259,7 @@ def configure_args(self):
|
||||
return args
|
||||
|
||||
|
||||
class NMakeBuilder(BaseBuilder, nmake.NMakeBuilder):
|
||||
class NMakeBuilder(AnyBuilder, nmake.NMakeBuilder):
|
||||
phases = ("configure", "build", "install")
|
||||
|
||||
@property
|
||||
|
@@ -423,7 +423,7 @@ def setup_build_environment(self, env):
|
||||
@run_after("build")
|
||||
@on_package_attributes(run_tests=True)
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
# The test suite contains a lot of tests. We select only those
|
||||
# that are cheap. Note this requires MPI and 6 processes
|
||||
ctest("--output-on-failure", "-L", "ESSENTIAL")
|
||||
|
@@ -160,5 +160,5 @@ def cmake_args(self):
|
||||
def check(self):
|
||||
"""Unit tests fail when run in parallel."""
|
||||
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test", parallel=False)
|
||||
|
@@ -36,5 +36,5 @@ def setup_run_environment(self, env):
|
||||
env.set("NCIO_LIBDIR", lib[0])
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -48,5 +48,5 @@ def cmake_args(self):
|
||||
return args
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -26,5 +26,5 @@ class Nemsiogfs(CMakePackage):
|
||||
depends_on("nemsio")
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -9,7 +9,6 @@
|
||||
|
||||
from llnl.util.lang import dedupe
|
||||
|
||||
import spack.builder
|
||||
from spack.build_systems import autotools, cmake
|
||||
from spack.package import *
|
||||
from spack.util.environment import filter_system_paths
|
||||
@@ -291,7 +290,7 @@ def setup_run_environment(self, env):
|
||||
env.append_path("HDF5_PLUGIN_PATH", self.prefix.plugins)
|
||||
|
||||
def flag_handler(self, name, flags):
|
||||
if self.builder.build_system == "autotools":
|
||||
if self.spec.satisfies("build_system=autotools"):
|
||||
if name == "cflags":
|
||||
if "+pic" in self.spec:
|
||||
flags.append(self.compiler.cc_pic_flag)
|
||||
@@ -305,7 +304,7 @@ def libs(self):
|
||||
return find_libraries("libnetcdf", root=self.prefix, shared=shared, recursive=True)
|
||||
|
||||
|
||||
class BaseBuilder(metaclass=spack.builder.PhaseCallbacksMeta):
|
||||
class AnyBuilder(BaseBuilder):
|
||||
def setup_dependent_build_environment(self, env, dependent_spec):
|
||||
# Some packages, e.g. ncview, refuse to build if the compiler path returned by nc-config
|
||||
# differs from the path to the compiler that the package should be built with. Therefore,
|
||||
@@ -329,7 +328,7 @@ def backup_nc_config(self):
|
||||
filter_compiler_wrappers("nc-config", relative_root="bin")
|
||||
|
||||
|
||||
class CMakeBuilder(BaseBuilder, cmake.CMakeBuilder):
|
||||
class CMakeBuilder(AnyBuilder, cmake.CMakeBuilder):
|
||||
def cmake_args(self):
|
||||
base_cmake_args = [
|
||||
self.define_from_variant("BUILD_SHARED_LIBS", "shared"),
|
||||
@@ -376,7 +375,7 @@ def patch_hdf5_pkgconfigcmake(self):
|
||||
filter_file(f"hdf5_hl-{config}", "hdf5_hl", *files, ignore_absent=True)
|
||||
|
||||
|
||||
class AutotoolsBuilder(BaseBuilder, autotools.AutotoolsBuilder):
|
||||
class AutotoolsBuilder(AnyBuilder, autotools.AutotoolsBuilder):
|
||||
@property
|
||||
def force_autoreconf(self):
|
||||
return any(self.spec.satisfies(s) for s in self.pkg._force_autoreconf_when)
|
||||
|
@@ -69,5 +69,5 @@ def cmake_args(self):
|
||||
@run_after("build")
|
||||
@on_package_attributes(run_tests=True)
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
ctest("--output-on-failure")
|
||||
|
@@ -34,5 +34,5 @@ class ProdUtil(CMakePackage):
|
||||
depends_on("w3emc", when="@2:")
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -3,7 +3,6 @@
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
import spack.builder
|
||||
from spack.build_systems import autotools, cmake
|
||||
from spack.package import *
|
||||
|
||||
@@ -145,7 +144,7 @@ def setup_run_environment(self, env):
|
||||
env.set("PROJ_LIB", self.prefix.share.proj)
|
||||
|
||||
|
||||
class BaseBuilder(metaclass=spack.builder.PhaseCallbacksMeta):
|
||||
class AnyBuilder(BaseBuilder):
|
||||
def setup_build_environment(self, env):
|
||||
env.set("PROJ_LIB", join_path(self.pkg.stage.source_path, "nad"))
|
||||
|
||||
@@ -154,7 +153,7 @@ def install_datum_grids(self):
|
||||
install_tree(join_path("share", "proj"), self.prefix.share.proj)
|
||||
|
||||
|
||||
class CMakeBuilder(BaseBuilder, cmake.CMakeBuilder):
|
||||
class CMakeBuilder(AnyBuilder, cmake.CMakeBuilder):
|
||||
def cmake_args(self):
|
||||
shared_arg = "BUILD_SHARED_LIBS" if self.spec.satisfies("@7:") else "BUILD_LIBPROJ_SHARED"
|
||||
args = [
|
||||
@@ -177,7 +176,7 @@ def cmake_args(self):
|
||||
return args
|
||||
|
||||
|
||||
class AutotoolsBuilder(BaseBuilder, autotools.AutotoolsBuilder):
|
||||
class AutotoolsBuilder(AnyBuilder, autotools.AutotoolsBuilder):
|
||||
def configure_args(self):
|
||||
args = []
|
||||
|
||||
|
@@ -448,7 +448,7 @@ def cmake_args(self):
|
||||
@property
|
||||
def build_relpath(self):
|
||||
"""Relative path to the cmake build subdirectory."""
|
||||
return join_path("..", self.builder.build_dirname)
|
||||
return join_path("..", self.build_dirname)
|
||||
|
||||
@run_after("install")
|
||||
def setup_build_tests(self):
|
||||
|
@@ -77,7 +77,7 @@ class Rocthrust(CMakePackage):
|
||||
depends_on(f"rocm-cmake@{ver}:", type="build", when=f"@{ver}")
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
||||
def setup_build_environment(self, env):
|
||||
|
@@ -46,5 +46,5 @@ def flag_handler(self, name, flags):
|
||||
return (None, None, flags)
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -44,5 +44,5 @@ def flag_handler(self, name, flags):
|
||||
return (None, None, flags)
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -66,5 +66,5 @@ def cmake_args(self):
|
||||
return args
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -226,7 +226,7 @@ def _test_example(self, test_prog, test_cmd, pre_args=[]):
|
||||
)
|
||||
|
||||
with working_dir(test_dir):
|
||||
opts = self.builder.std_cmake_args + self.cmake_args() + ["."]
|
||||
opts = self.std_cmake_args + self.cmake_args() + ["."]
|
||||
cmake = self.spec["cmake"].command
|
||||
cmake(*opts)
|
||||
|
||||
|
@@ -743,7 +743,7 @@ def libs(self):
|
||||
@on_package_attributes(run_tests=True)
|
||||
def check_test_install(self):
|
||||
"""Perform test_install on the build."""
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test_install")
|
||||
|
||||
@property
|
||||
|
@@ -84,7 +84,7 @@ def test_example(self):
|
||||
superlu()
|
||||
|
||||
|
||||
class BaseBuilder(metaclass=spack.builder.PhaseCallbacksMeta):
|
||||
class AnyBuilder(BaseBuilder):
|
||||
@run_after("install")
|
||||
def setup_standalone_tests(self):
|
||||
"""Set up and copy example source files after the package is installed
|
||||
@@ -138,7 +138,7 @@ def _make_hdr_for_test(self, lib):
|
||||
]
|
||||
|
||||
|
||||
class CMakeBuilder(BaseBuilder, spack.build_systems.cmake.CMakeBuilder):
|
||||
class CMakeBuilder(AnyBuilder, spack.build_systems.cmake.CMakeBuilder):
|
||||
def cmake_args(self):
|
||||
if self.pkg.version > Version("5.2.1"):
|
||||
_blaslib_key = "enable_internal_blaslib"
|
||||
@@ -153,7 +153,7 @@ def cmake_args(self):
|
||||
return args
|
||||
|
||||
|
||||
class GenericBuilder(BaseBuilder, spack.build_systems.generic.GenericBuilder):
|
||||
class GenericBuilder(AnyBuilder, spack.build_systems.generic.GenericBuilder):
|
||||
def install(self, pkg, spec, prefix):
|
||||
"""Use autotools before version 5"""
|
||||
# Define make.inc file
|
||||
|
@@ -14,22 +14,20 @@
|
||||
is_windows = sys.platform == "win32"
|
||||
|
||||
|
||||
class TclHelper:
|
||||
@staticmethod
|
||||
def find_script_dir(spec):
|
||||
# Put more-specific prefixes first
|
||||
check_prefixes = [
|
||||
join_path(spec.prefix, "share", "tcl{0}".format(spec.package.version.up_to(2))),
|
||||
spec.prefix,
|
||||
]
|
||||
for prefix in check_prefixes:
|
||||
result = find_first(prefix, "init.tcl")
|
||||
if result:
|
||||
return os.path.dirname(result)
|
||||
raise RuntimeError("Cannot locate init.tcl")
|
||||
def find_script_dir(spec: Spec) -> str:
|
||||
# Put more-specific prefixes first
|
||||
check_prefixes = [
|
||||
join_path(spec.prefix, "share", "tcl{0}".format(spec.package.version.up_to(2))),
|
||||
spec.prefix,
|
||||
]
|
||||
for prefix in check_prefixes:
|
||||
result = find_first(prefix, "init.tcl")
|
||||
if result:
|
||||
return os.path.dirname(result)
|
||||
raise RuntimeError("Cannot locate init.tcl")
|
||||
|
||||
|
||||
class Tcl(AutotoolsPackage, NMakePackage, SourceforgePackage, TclHelper):
|
||||
class Tcl(AutotoolsPackage, NMakePackage, SourceforgePackage):
|
||||
"""Tcl (Tool Command Language) is a very powerful but easy to learn dynamic
|
||||
programming language, suitable for a very wide range of uses, including web and
|
||||
desktop applications, networking, administration, testing and many more. Open source
|
||||
@@ -105,7 +103,7 @@ def setup_run_environment(self, env):
|
||||
"""
|
||||
# When using tkinter from within spack provided python+tkinter,
|
||||
# python will not be able to find Tcl unless TCL_LIBRARY is set.
|
||||
env.set("TCL_LIBRARY", TclHelper.find_script_dir(self.spec))
|
||||
env.set("TCL_LIBRARY", find_script_dir(self.spec))
|
||||
|
||||
def setup_dependent_run_environment(self, env, dependent_spec):
|
||||
"""Set TCLLIBPATH to include the tcl-shipped directory for
|
||||
@@ -123,7 +121,7 @@ def setup_dependent_run_environment(self, env, dependent_spec):
|
||||
env.prepend_path("TCLLIBPATH", tcllibpath, separator=" ")
|
||||
|
||||
|
||||
class BaseBuilder(TclHelper, metaclass=spack.builder.PhaseCallbacksMeta):
|
||||
class AnyBuilder(BaseBuilder):
|
||||
@run_after("install")
|
||||
def symlink_tclsh(self):
|
||||
# There's some logic regarding this suffix in the build system
|
||||
@@ -151,7 +149,7 @@ def setup_dependent_build_environment(self, env, dependent_spec):
|
||||
* https://wiki.tcl-lang.org/page/TCL_LIBRARY
|
||||
* https://wiki.tcl-lang.org/page/TCLLIBPATH
|
||||
"""
|
||||
env.set("TCL_LIBRARY", TclHelper.find_script_dir(self.spec))
|
||||
env.set("TCL_LIBRARY", find_script_dir(self.spec))
|
||||
|
||||
# If we set TCLLIBPATH, we must also ensure that the corresponding
|
||||
# tcl is found in the build environment. This to prevent cases
|
||||
@@ -182,7 +180,7 @@ def setup_dependent_build_environment(self, env, dependent_spec):
|
||||
env.prepend_path("TCLLIBPATH", tcllibpath, separator=" ")
|
||||
|
||||
|
||||
class AutotoolsBuilder(BaseBuilder, spack.build_systems.autotools.AutotoolsBuilder):
|
||||
class AutotoolsBuilder(AnyBuilder, spack.build_systems.autotools.AutotoolsBuilder):
|
||||
configure_directory = "unix"
|
||||
|
||||
def install(self, pkg, spec, prefix):
|
||||
@@ -215,7 +213,7 @@ def install(self, pkg, spec, prefix):
|
||||
make("clean")
|
||||
|
||||
|
||||
class NMakeBuilder(BaseBuilder, spack.build_systems.nmake.NMakeBuilder):
|
||||
class NMakeBuilder(AnyBuilder, spack.build_systems.nmake.NMakeBuilder):
|
||||
build_targets = ["all"]
|
||||
install_targets = ["install"]
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from spack.build_systems.cmake import CMakeBuilder
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@@ -249,7 +250,7 @@ def cmake_args(self):
|
||||
options.append("-DCMAKE_CUDA_HOST_COMPILER={0}".format(env["SPACK_CXX"]))
|
||||
|
||||
if spec.satisfies("@1.9.0:") and spec.satisfies("^cmake@3.18:"):
|
||||
options.append(self.builder.define_cuda_architectures(self))
|
||||
options.append(CMakeBuilder.define_cuda_architectures(self))
|
||||
|
||||
else:
|
||||
# VTKm_CUDA_Architecture only accepts a single CUDA arch
|
||||
@@ -269,7 +270,7 @@ def cmake_args(self):
|
||||
|
||||
# hip support
|
||||
if "+rocm" in spec:
|
||||
options.append(self.builder.define_hip_architectures(self))
|
||||
options.append(CMakeBuilder.define_hip_architectures(self))
|
||||
|
||||
return options
|
||||
|
||||
|
@@ -95,5 +95,5 @@ def cmake_args(self):
|
||||
return args
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
make("test")
|
||||
|
@@ -101,5 +101,5 @@ def enabled(pkg):
|
||||
return args
|
||||
|
||||
def check(self):
|
||||
with working_dir(self.builder.build_directory):
|
||||
with working_dir(self.build_directory):
|
||||
ctest("--output-on-failure")
|
||||
|
Reference in New Issue
Block a user