diff --git a/lib/spack/docs/requirements.txt b/lib/spack/docs/requirements.txt index 02bf9824037..adcf16f3798 100644 --- a/lib/spack/docs/requirements.txt +++ b/lib/spack/docs/requirements.txt @@ -1,4 +1,4 @@ -sphinx==7.2.4 +sphinx==7.2.5 sphinxcontrib-programoutput==0.17 sphinx_design==0.5.0 sphinx-rtd-theme==1.3.0 diff --git a/lib/spack/spack/compilers/msvc.py b/lib/spack/spack/compilers/msvc.py index c9efa264eea..7e91e8eca2e 100644 --- a/lib/spack/spack/compilers/msvc.py +++ b/lib/spack/spack/compilers/msvc.py @@ -29,6 +29,90 @@ } +class CmdCall: + """Compose a call to `cmd` for an ordered series of cmd commands/scripts""" + + def __init__(self, *cmds): + if not cmds: + raise RuntimeError( + """Attempting to run commands from CMD without specifying commands. + Please add commands to be run.""" + ) + self._cmds = cmds + + def __call__(self): + out = subprocess.check_output(self.cmd_line, stderr=subprocess.STDOUT) # novermin + return out.decode("utf-16le", errors="replace") # novermin + + @property + def cmd_line(self): + base_call = "cmd /u /c " + commands = " && ".join([x.command_str() for x in self._cmds]) + # If multiple commands are being invoked by a single subshell + # they must be encapsulated by a double quote. Always double + # quote to be sure of proper handling + # cmd will properly resolve nested double quotes as needed + # + # `set`` writes out the active env to the subshell stdout, + # and in this context we are always trying to obtain env + # state so it should always be appended + return base_call + f'"{commands} && set"' + + +class VarsInvocation: + def __init__(self, script): + self._script = script + + def command_str(self): + return f'"{self._script}"' + + @property + def script(self): + return self._script + + +class VCVarsInvocation(VarsInvocation): + def __init__(self, script, arch, msvc_version): + super(VCVarsInvocation, self).__init__(script) + self._arch = arch + self._msvc_version = msvc_version + + @property + def sdk_ver(self): + """Accessor for Windows SDK version property + + Note: This property may not be set by + the calling context and as such this property will + return an empty string + + This property will ONLY be set if the SDK package + is a dependency somewhere in the Spack DAG of the package + for which we are constructing an MSVC compiler env. + Otherwise this property should be unset to allow the VCVARS + script to use its internal heuristics to determine appropriate + SDK version + """ + if getattr(self, "_sdk_ver", None): + return self._sdk_ver + ".0" + return "" + + @sdk_ver.setter + def sdk_ver(self, val): + self._sdk_ver = val + + @property + def arch(self): + return self._arch + + @property + def vcvars_ver(self): + return f"-vcvars_ver={self._msvc_version}" + + def command_str(self): + script = super(VCVarsInvocation, self).command_str() + return f"{script} {self.arch} {self.sdk_ver} {self.vcvars_ver}" + + def get_valid_fortran_pth(comp_ver): cl_ver = str(comp_ver) sort_fn = lambda fc_ver: StrictVersion(fc_ver) @@ -75,22 +159,48 @@ class Msvc(Compiler): # file based on compiler executable path. def __init__(self, *args, **kwargs): - new_pth = [pth if pth else get_valid_fortran_pth(args[0].version) for pth in args[3]] - args[3][:] = new_pth + # This positional argument "paths" is later parsed and process by the base class + # via the call to `super` later in this method + paths = args[3] + # This positional argument "cspec" is also parsed and handled by the base class + # constructor + cspec = args[0] + new_pth = [pth if pth else get_valid_fortran_pth(cspec.version) for pth in paths] + paths[:] = new_pth super().__init__(*args, **kwargs) - if os.getenv("ONEAPI_ROOT"): + # To use the MSVC compilers, VCVARS must be invoked + # VCVARS is located at a fixed location, referencable + # idiomatically by the following relative path from the + # compiler. + # Spack first finds the compilers via VSWHERE + # and stores their path, but their respective VCVARS + # file must be invoked before useage. + env_cmds = [] + compiler_root = os.path.join(self.cc, "../../../../../../..") + vcvars_script_path = os.path.join(compiler_root, "Auxiliary", "Build", "vcvars64.bat") + # get current platform architecture and format for vcvars argument + arch = spack.platforms.real_host().default.lower() + arch = arch.replace("-", "_") + self.vcvars_call = VCVarsInvocation(vcvars_script_path, arch, self.msvc_version) + env_cmds.append(self.vcvars_call) + # Below is a check for a valid fortran path + # paths has c, cxx, fc, and f77 paths in that order + # paths[2] refers to the fc path and is a generic check + # for a fortran compiler + if paths[2]: # If this found, it sets all the vars - self.setvarsfile = os.path.join(os.getenv("ONEAPI_ROOT"), "setvars.bat") - else: - # To use the MSVC compilers, VCVARS must be invoked - # VCVARS is located at a fixed location, referencable - # idiomatically by the following relative path from the - # compiler. - # Spack first finds the compilers via VSWHERE - # and stores their path, but their respective VCVARS - # file must be invoked before useage. - self.setvarsfile = os.path.abspath(os.path.join(self.cc, "../../../../../../..")) - self.setvarsfile = os.path.join(self.setvarsfile, "Auxiliary", "Build", "vcvars64.bat") + oneapi_root = os.getenv("ONEAPI_ROOT") + oneapi_root_setvars = os.path.join(oneapi_root, "setvars.bat") + oneapi_version_setvars = os.path.join( + oneapi_root, "compiler", str(self.ifx_version), "env", "vars.bat" + ) + # order matters here, the specific version env must be invoked first, + # otherwise it will be ignored if the root setvars sets up the oneapi + # env first + env_cmds.extend( + [VarsInvocation(oneapi_version_setvars), VarsInvocation(oneapi_root_setvars)] + ) + self.msvc_compiler_environment = CmdCall(*env_cmds) @property def msvc_version(self): @@ -119,16 +229,30 @@ def platform_toolset_ver(self): """ return self.msvc_version[:2].joined.string[:3] - @property - def cl_version(self): - """Cl toolset version""" + def _compiler_version(self, compiler): + """Returns version object for given compiler""" + # ignore_errors below is true here due to ifx's + # non zero return code if it is not provided + # and input file return Version( re.search( Msvc.version_regex, - spack.compiler.get_compiler_version_output(self.cc, version_arg=None), + spack.compiler.get_compiler_version_output( + compiler, version_arg=None, ignore_errors=True + ), ).group(1) ) + @property + def cl_version(self): + """Cl toolset version""" + return self._compiler_version(self.cc) + + @property + def ifx_version(self): + """Ifx compiler version associated with this version of MSVC""" + return self._compiler_version(self.fc) + @property def vs_root(self): # The MSVC install root is located at a fix level above the compiler @@ -146,27 +270,12 @@ def setup_custom_environment(self, pkg, env): # output, sort into dictionary, use that to make the build # environment. - # get current platform architecture and format for vcvars argument - arch = spack.platforms.real_host().default.lower() - arch = arch.replace("-", "_") # vcvars can target specific sdk versions, force it to pick up concretized sdk # version, if needed by spec - sdk_ver = ( - "" - if "win-sdk" not in pkg.spec or pkg.name == "win-sdk" - else pkg.spec["win-sdk"].version.string + ".0" - ) - # provide vcvars with msvc version selected by concretization, - # not whatever it happens to pick up on the system (highest available version) - out = subprocess.check_output( # novermin - 'cmd /u /c "{}" {} {} {} && set'.format( - self.setvarsfile, arch, sdk_ver, "-vcvars_ver=%s" % self.msvc_version - ), - stderr=subprocess.STDOUT, - ) - if sys.version_info[0] >= 3: - out = out.decode("utf-16le", errors="replace") # novermin + if pkg.name != "win-sdk" and "win-sdk" in pkg.spec: + self.vcvars_call.sdk_ver = pkg.spec["win-sdk"].version.string + out = self.msvc_compiler_environment() int_env = dict( (key, value) for key, _, value in (line.partition("=") for line in out.splitlines()) diff --git a/lib/spack/spack/directives.py b/lib/spack/spack/directives.py index d6d7d81298c..ccc913a1fec 100644 --- a/lib/spack/spack/directives.py +++ b/lib/spack/spack/directives.py @@ -42,6 +42,7 @@ class OpenMpi(Package): import spack.patch import spack.spec import spack.url +import spack.util.crypto import spack.variant from spack.dependency import Dependency, canonical_deptype, default_deptype from spack.fetch_strategy import from_kwargs @@ -407,10 +408,7 @@ def version( def _execute_version(pkg, ver, **kwargs): if ( - any( - s in kwargs - for s in ("sha256", "sha384", "sha512", "md5", "sha1", "sha224", "checksum") - ) + (any(s in kwargs for s in spack.util.crypto.hashes) or "checksum" in kwargs) and hasattr(pkg, "has_code") and not pkg.has_code ): diff --git a/lib/spack/spack/environment/environment.py b/lib/spack/spack/environment/environment.py index ea4ac49a3ac..8eb7edab409 100644 --- a/lib/spack/spack/environment/environment.py +++ b/lib/spack/spack/environment/environment.py @@ -2664,6 +2664,26 @@ def __init__(self, manifest_dir: Union[pathlib.Path, str]) -> None: self.yaml_content = with_defaults_added self.changed = False + def _all_matches(self, user_spec: str) -> List[str]: + """Maps the input string to the first equivalent user spec in the manifest, + and returns it. + + Args: + user_spec: user spec to be found + + Raises: + ValueError: if no equivalent match is found + """ + result = [] + for yaml_spec_str in self.pristine_configuration["specs"]: + if Spec(yaml_spec_str) == Spec(user_spec): + result.append(yaml_spec_str) + + if not result: + raise ValueError(f"cannot find a spec equivalent to {user_spec}") + + return result + def add_user_spec(self, user_spec: str) -> None: """Appends the user spec passed as input to the list of root specs. @@ -2684,8 +2704,9 @@ def remove_user_spec(self, user_spec: str) -> None: SpackEnvironmentError: when the user spec is not in the list """ try: - self.pristine_configuration["specs"].remove(user_spec) - self.configuration["specs"].remove(user_spec) + for key in self._all_matches(user_spec): + self.pristine_configuration["specs"].remove(key) + self.configuration["specs"].remove(key) except ValueError as e: msg = f"cannot remove {user_spec} from {self}, no such spec exists" raise SpackEnvironmentError(msg) from e diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 50d316a625b..4ceb32d7697 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -13,7 +13,7 @@ import re import types import warnings -from typing import List, NamedTuple +from typing import List, NamedTuple, Tuple, Union import archspec.cpu @@ -44,15 +44,18 @@ import spack.repo import spack.spec import spack.store -import spack.traverse +import spack.util.crypto import spack.util.path import spack.util.timer import spack.variant import spack.version as vn import spack.version.git_ref_lookup +from spack import traverse from .counter import FullDuplicatesCounter, MinimalDuplicatesCounter, NoDuplicatesCounter +GitOrStandardVersion = Union[spack.version.GitVersion, spack.version.StandardVersion] + # these are from clingo.ast and bootstrapped later ASTType = None parse_files = None @@ -569,6 +572,41 @@ def keyfn(x): return normalized_yaml +def _is_checksummed_git_version(v): + return isinstance(v, vn.GitVersion) and v.is_commit + + +def _is_checksummed_version(version_info: Tuple[GitOrStandardVersion, dict]): + """Returns true iff the version is not a moving target""" + version, info = version_info + if isinstance(version, spack.version.StandardVersion): + if any(h in info for h in spack.util.crypto.hashes.keys()) or "checksum" in info: + return True + return "commit" in info and len(info["commit"]) == 40 + return _is_checksummed_git_version(version) + + +def _concretization_version_order(version_info: Tuple[GitOrStandardVersion, dict]): + """Version order key for concretization, where preferred > not preferred, + not deprecated > deprecated, finite > any infinite component; only if all are + the same, do we use default version ordering.""" + version, info = version_info + return ( + info.get("preferred", False), + not info.get("deprecated", False), + not version.isdevelop(), + version, + ) + + +def _spec_with_default_name(spec_str, name): + """Return a spec with a default name if none is provided, used for requirement specs""" + spec = spack.spec.Spec(spec_str) + if not spec.name: + spec.name = name + return spec + + def bootstrap_clingo(): global clingo, ASTType, parse_files @@ -1857,30 +1895,27 @@ class Body: return clauses - def build_version_dict(self, possible_pkgs): + def define_package_versions_and_validate_preferences( + self, possible_pkgs, require_checksum: bool + ): """Declare any versions in specs not declared in packages.""" packages_yaml = spack.config.get("packages") - packages_yaml = _normalize_packages_yaml(packages_yaml) for pkg_name in possible_pkgs: pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name) # All the versions from the corresponding package.py file. Since concepts # like being a "develop" version or being preferred exist only at a # package.py level, sort them in this partial list here - def key_fn(item): - version, info = item - # When COMPARING VERSIONS, the '@develop' version is always - # larger than other versions. BUT when CONCRETIZING, the largest - # NON-develop version is selected by default. - return ( - info.get("preferred", False), - not info.get("deprecated", False), - not version.isdevelop(), - version, - ) + package_py_versions = sorted( + pkg_cls.versions.items(), key=_concretization_version_order, reverse=True + ) - for idx, item in enumerate(sorted(pkg_cls.versions.items(), key=key_fn, reverse=True)): - v, version_info = item + if require_checksum and pkg_cls.has_code: + package_py_versions = [ + x for x in package_py_versions if _is_checksummed_version(x) + ] + + for idx, (v, version_info) in enumerate(package_py_versions): self.possible_versions[pkg_name].add(v) self.declared_versions[pkg_name].append( DeclaredVersion(version=v, idx=idx, origin=Provenance.PACKAGE_PY) @@ -1889,22 +1924,26 @@ def key_fn(item): if deprecated: self.deprecated_versions[pkg_name].add(v) - # All the preferred version from packages.yaml, versions in external - # specs will be computed later - version_preferences = packages_yaml.get(pkg_name, {}).get("version", []) + if pkg_name not in packages_yaml or "version" not in packages_yaml[pkg_name]: + continue + version_defs = [] - pkg_class = spack.repo.PATH.get_pkg_class(pkg_name) - for vstr in version_preferences: + + for vstr in packages_yaml[pkg_name]["version"]: v = vn.ver(vstr) + if isinstance(v, vn.GitVersion): - version_defs.append(v) + if not require_checksum or v.is_commit: + version_defs.append(v) else: - satisfying_versions = self._check_for_defined_matching_versions(pkg_class, v) - # Amongst all defined versions satisfying this specific - # preference, the highest-numbered version is the - # most-preferred: therefore sort satisfying versions - # from greatest to least - version_defs.extend(sorted(satisfying_versions, reverse=True)) + matches = [x for x in self.possible_versions[pkg_name] if x.satisfies(v)] + matches.sort(reverse=True) + if not matches: + raise spack.config.ConfigError( + f"Preference for version {v} does not match any known " + f"version of {pkg_name} (in its package.py or any external)" + ) + version_defs.extend(matches) for weight, vdef in enumerate(llnl.util.lang.dedupe(version_defs)): self.declared_versions[pkg_name].append( @@ -1912,31 +1951,9 @@ def key_fn(item): ) self.possible_versions[pkg_name].add(vdef) - def _check_for_defined_matching_versions(self, pkg_class, v): - """Given a version specification (which may be a concrete version, - range, etc.), determine if any package.py version declarations - or externals define a version which satisfies it. - - This is primarily for determining whether a version request (e.g. - version preferences, which should not themselves define versions) - refers to a defined version. - - This function raises an exception if no satisfying versions are - found. - """ - pkg_name = pkg_class.name - satisfying_versions = list(x for x in pkg_class.versions if x.satisfies(v)) - satisfying_versions.extend(x for x in self.possible_versions[pkg_name] if x.satisfies(v)) - if not satisfying_versions: - raise spack.config.ConfigError( - "Preference for version {0} does not match any version" - " defined for {1} (in its package.py or any external)".format(str(v), pkg_name) - ) - return satisfying_versions - - def add_concrete_versions_from_specs(self, specs, origin): + def define_ad_hoc_versions_from_specs(self, specs, origin, require_checksum: bool): """Add concrete versions to possible versions from lists of CLI/dev specs.""" - for s in spack.traverse.traverse_nodes(specs): + for s in traverse.traverse_nodes(specs): # If there is a concrete version on the CLI *that we know nothing # about*, add it to the known versions. Use idx=0, which is the # best possible, so they're guaranteed to be used preferentially. @@ -1945,9 +1962,13 @@ def add_concrete_versions_from_specs(self, specs, origin): if version is None or any(v == version for v in self.possible_versions[s.name]): continue - self.declared_versions[s.name].append( - DeclaredVersion(version=version, idx=0, origin=origin) - ) + if require_checksum and not _is_checksummed_git_version(version): + raise UnsatisfiableSpecError( + s.format("No matching version for constraint {name}{@versions}") + ) + + declared = DeclaredVersion(version=version, idx=0, origin=origin) + self.declared_versions[s.name].append(declared) self.possible_versions[s.name].add(version) def _supported_targets(self, compiler_name, compiler_version, targets): @@ -2144,7 +2165,7 @@ def generate_possible_compilers(self, specs): # add compiler specs from the input line to possibilities if we # don't require compilers to exist. strict = spack.concretize.Concretizer().check_for_compiler_existence - for s in spack.traverse.traverse_nodes(specs): + for s in traverse.traverse_nodes(specs): # we don't need to validate compilers for already-built specs if s.concrete or not s.compiler: continue @@ -2394,13 +2415,12 @@ def setup(self, driver, specs, reuse=None): self.provider_requirements() self.external_packages() - # traverse all specs and packages to build dict of possible versions - self.build_version_dict(self.pkgs) - self.add_concrete_versions_from_specs(specs, Provenance.SPEC) - self.add_concrete_versions_from_specs(dev_specs, Provenance.DEV_SPEC) - - req_version_specs = self._get_versioned_specs_from_pkg_requirements() - self.add_concrete_versions_from_specs(req_version_specs, Provenance.PACKAGE_REQUIREMENT) + # TODO: make a config option for this undocumented feature + require_checksum = "SPACK_CONCRETIZER_REQUIRE_CHECKSUM" in os.environ + self.define_package_versions_and_validate_preferences(self.pkgs, require_checksum) + self.define_ad_hoc_versions_from_specs(specs, Provenance.SPEC, require_checksum) + self.define_ad_hoc_versions_from_specs(dev_specs, Provenance.DEV_SPEC, require_checksum) + self.validate_and_define_versions_from_requirements(require_checksum) self.gen.h1("Package Constraints") for pkg in sorted(self.pkgs): @@ -2449,78 +2469,68 @@ def literal_specs(self, specs): if self.concretize_everything: self.gen.fact(fn.solve_literal(idx)) - def _get_versioned_specs_from_pkg_requirements(self): - """If package requirements mention versions that are not mentioned + def validate_and_define_versions_from_requirements(self, require_checksum: bool): + """If package requirements mention concrete versions that are not mentioned elsewhere, then we need to collect those to mark them as possible - versions. - """ - req_version_specs = list() - config = spack.config.get("packages") - for pkg_name, d in config.items(): - if pkg_name == "all": + versions. If they are abstract and statically have no match, then we + need to throw an error. This function assumes all possible versions are already + registered in self.possible_versions.""" + for pkg_name, d in spack.config.get("packages").items(): + if pkg_name == "all" or "require" not in d: continue - if "require" in d: - req_version_specs.extend(self._specs_from_requires(pkg_name, d["require"])) - return req_version_specs + + for s in traverse.traverse_nodes(self._specs_from_requires(pkg_name, d["require"])): + name, versions = s.name, s.versions + + if name not in self.pkgs or versions == spack.version.any_version: + continue + + s.attach_git_version_lookup() + v = versions.concrete + + if not v: + # If the version is not concrete, check it's statically concretizable. If + # not throw an error, which is just so that users know they need to change + # their config, instead of getting a hard to decipher concretization error. + if not any(x for x in self.possible_versions[name] if x.satisfies(versions)): + raise spack.config.ConfigError( + f"Version requirement {versions} on {pkg_name} for {name} " + f"cannot match any known version from package.py or externals" + ) + continue + + if v in self.possible_versions[name]: + continue + + # If concrete an not yet defined, conditionally define it, like we do for specs + # from the command line. + if not require_checksum or _is_checksummed_git_version(v): + self.declared_versions[name].append( + DeclaredVersion(version=v, idx=0, origin=Provenance.PACKAGE_REQUIREMENT) + ) + self.possible_versions[name].add(v) def _specs_from_requires(self, pkg_name, section): - """Collect specs from requirements which define versions (i.e. those that - have a concrete version). Requirements can define *new* versions if - they are included as part of an equivalence (hash=number) but not - otherwise. - """ + """Collect specs from a requirement rule""" if isinstance(section, str): - spec = spack.spec.Spec(section) - if not spec.name: - spec.name = pkg_name - extracted_specs = [spec] - else: - spec_strs = [] - for spec_group in section: - if isinstance(spec_group, str): - spec_strs.append(spec_group) - else: - # Otherwise it is an object. The object can contain a single - # "spec" constraint, or a list of them with "any_of" or - # "one_of" policy. - if "spec" in spec_group: - new_constraints = [spec_group["spec"]] - else: - key = "one_of" if "one_of" in spec_group else "any_of" - new_constraints = spec_group[key] - spec_strs.extend(new_constraints) + yield _spec_with_default_name(section, pkg_name) + return - extracted_specs = [] - for spec_str in spec_strs: - spec = spack.spec.Spec(spec_str) - if not spec.name: - spec.name = pkg_name - extracted_specs.append(spec) - - version_specs = [] - for spec in extracted_specs: - if spec.versions.concrete: - # Note: this includes git versions - version_specs.append(spec) + for spec_group in section: + if isinstance(spec_group, str): + yield _spec_with_default_name(spec_group, pkg_name) continue - # Prefer spec's name if it exists, in case the spec is - # requiring a specific implementation inside of a virtual section - # e.g. packages:mpi:require:openmpi@4.0.1 - pkg_class = spack.repo.PATH.get_pkg_class(spec.name or pkg_name) - satisfying_versions = self._check_for_defined_matching_versions( - pkg_class, spec.versions - ) + # Otherwise it is an object. The object can contain a single + # "spec" constraint, or a list of them with "any_of" or + # "one_of" policy. + if "spec" in spec_group: + yield _spec_with_default_name(spec_group["spec"], pkg_name) + continue - # Version ranges ("@1.3" without the "=", "@1.2:1.4") and lists - # will end up here - ordered_satisfying_versions = sorted(satisfying_versions, reverse=True) - vspecs = list(spack.spec.Spec("@{0}".format(x)) for x in ordered_satisfying_versions) - version_specs.extend(vspecs) - - for spec in version_specs: - spec.attach_git_version_lookup() - return version_specs + key = "one_of" if "one_of" in spec_group else "any_of" + for s in spec_group[key]: + yield _spec_with_default_name(s, pkg_name) class SpecBuilder: diff --git a/lib/spack/spack/spec_list.py b/lib/spack/spack/spec_list.py index 6cad15e14ea..3f60d572492 100644 --- a/lib/spack/spack/spec_list.py +++ b/lib/spack/spack/spec_list.py @@ -97,8 +97,10 @@ def remove(self, spec): msg += "Either %s is not in %s or %s is " % (spec, self.name, spec) msg += "expanded from a matrix and cannot be removed directly." raise SpecListError(msg) - assert len(remove) == 1 - self.yaml_list.remove(remove[0]) + + # Remove may contain more than one string representation of the same spec + for item in remove: + self.yaml_list.remove(item) # invalidate cache variables when we change the list self._expanded_list = None diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 267cef61547..c2bc9dd5918 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -21,10 +21,11 @@ import spack.hash_types as ht import spack.platforms import spack.repo +import spack.solver.asp import spack.variant as vt from spack.concretize import find_spec from spack.spec import CompilerSpec, Spec -from spack.version import ver +from spack.version import Version, ver def check_spec(abstract, concrete): @@ -1610,8 +1611,6 @@ def test_reuse_with_unknown_package_dont_raise(self, tmpdir, monkeypatch): ) @pytest.mark.only_clingo("Original concretizer cannot concretize in rounds") def test_best_effort_coconcretize(self, specs, expected): - import spack.solver.asp - specs = [Spec(s) for s in specs] solver = spack.solver.asp.Solver() solver.reuse = False @@ -1655,8 +1654,6 @@ def test_best_effort_coconcretize(self, specs, expected): @pytest.mark.only_clingo("Original concretizer cannot concretize in rounds") def test_best_effort_coconcretize_preferences(self, specs, expected_spec, occurances): """Test package preferences during coconcretization.""" - import spack.solver.asp - specs = [Spec(s) for s in specs] solver = spack.solver.asp.Solver() solver.reuse = False @@ -1672,8 +1669,6 @@ def test_best_effort_coconcretize_preferences(self, specs, expected_spec, occura @pytest.mark.only_clingo("Use case not supported by the original concretizer") def test_coconcretize_reuse_and_virtuals(self): - import spack.solver.asp - reusable_specs = [] for s in ["mpileaks ^mpich", "zmpi"]: reusable_specs.extend(Spec(s).concretized().traverse(root=True)) @@ -1694,8 +1689,6 @@ def test_misleading_error_message_on_version(self, mutable_database): # For this bug to be triggered we need a reusable dependency # that is not optimal in terms of optimization scores. # We pick an old version of "b" - import spack.solver.asp - reusable_specs = [Spec("non-existing-conditional-dep@1.0").concretized()] root_spec = Spec("non-existing-conditional-dep@2.0") @@ -1711,8 +1704,6 @@ def test_misleading_error_message_on_version(self, mutable_database): @pytest.mark.only_clingo("Use case not supported by the original concretizer") def test_version_weight_and_provenance(self): """Test package preferences during coconcretization.""" - import spack.solver.asp - reusable_specs = [Spec(spec_str).concretized() for spec_str in ("b@0.9", "b@1.0")] root_spec = Spec("a foobar=bar") @@ -1744,8 +1735,6 @@ def test_version_weight_and_provenance(self): @pytest.mark.regression("31169") @pytest.mark.only_clingo("Use case not supported by the original concretizer") def test_not_reusing_incompatible_os_or_compiler(self): - import spack.solver.asp - root_spec = Spec("b") s = root_spec.concretized() wrong_compiler, wrong_os = s.copy(), s.copy() @@ -2126,6 +2115,14 @@ def test_virtuals_are_reconstructed_on_reuse(self, spec_str, mpi_name, database) assert len(mpi_edges) == 1 assert "mpi" in mpi_edges[0].virtuals + @pytest.mark.only_clingo("Use case not supported by the original concretizer") + def test_dont_define_new_version_from_input_if_checksum_required(self, working_env): + os.environ["SPACK_CONCRETIZER_REQUIRE_CHECKSUM"] = "yes" + with pytest.raises(spack.error.UnsatisfiableSpecError): + # normally spack concretizes to @=3.0 if it's not defined in package.py, except + # when checksums are required + Spec("a@=3.0").concretized() + @pytest.fixture() def duplicates_test_repository(): @@ -2220,3 +2217,39 @@ def test_solution_without_cycles(self): s = Spec("cycle-b").concretized() assert s["cycle-a"].satisfies("~cycle") assert s["cycle-b"].satisfies("+cycle") + + +@pytest.mark.parametrize( + "v_str,v_opts,checksummed", + [ + ("1.2.3", {"sha256": f"{1:064x}"}, True), + # it's not about the version being "infinite", + # but whether it has a digest + ("develop", {"sha256": f"{1:064x}"}, True), + # other hash types + ("1.2.3", {"checksum": f"{1:064x}"}, True), + ("1.2.3", {"md5": f"{1:032x}"}, True), + ("1.2.3", {"sha1": f"{1:040x}"}, True), + ("1.2.3", {"sha224": f"{1:056x}"}, True), + ("1.2.3", {"sha384": f"{1:096x}"}, True), + ("1.2.3", {"sha512": f"{1:0128x}"}, True), + # no digest key + ("1.2.3", {"bogus": f"{1:064x}"}, False), + # git version with full commit sha + ("1.2.3", {"commit": f"{1:040x}"}, True), + (f"{1:040x}=1.2.3", {}, True), + # git version with short commit sha + ("1.2.3", {"commit": f"{1:07x}"}, False), + (f"{1:07x}=1.2.3", {}, False), + # git tag is a moving target + ("1.2.3", {"tag": "v1.2.3"}, False), + ("1.2.3", {"tag": "v1.2.3", "commit": f"{1:07x}"}, False), + # git branch is a moving target + ("1.2.3", {"branch": "releases/1.2"}, False), + # git ref is a moving target + ("git.branch=1.2.3", {}, False), + ], +) +def test_drop_moving_targets(v_str, v_opts, checksummed): + v = Version(v_str) + assert spack.solver.asp._is_checksummed_version((v, v_opts)) == checksummed diff --git a/lib/spack/spack/test/concretize_requirements.py b/lib/spack/spack/test/concretize_requirements.py index 20b89d9e5f3..d7c1c883597 100644 --- a/lib/spack/spack/test/concretize_requirements.py +++ b/lib/spack/spack/test/concretize_requirements.py @@ -2,6 +2,7 @@ # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import os import pathlib import pytest @@ -299,9 +300,14 @@ def test_requirement_adds_version_satisfies( assert s1.satisfies("@2.2") +@pytest.mark.parametrize("require_checksum", (True, False)) def test_requirement_adds_git_hash_version( - concretize_scope, test_repo, mock_git_version_info, monkeypatch + require_checksum, concretize_scope, test_repo, mock_git_version_info, monkeypatch, working_env ): + # A full commit sha is a checksummed version, so this test should pass in both cases + if require_checksum: + os.environ["SPACK_CONCRETIZER_REQUIRE_CHECKSUM"] = "yes" + repo_path, filename, commits = mock_git_version_info monkeypatch.setattr( spack.package_base.PackageBase, "git", path_to_file_url(repo_path), raising=False diff --git a/lib/spack/spack/test/env.py b/lib/spack/spack/test/env.py index 4396557f8a3..e88af087619 100644 --- a/lib/spack/spack/test/env.py +++ b/lib/spack/spack/test/env.py @@ -13,7 +13,11 @@ import spack.environment as ev import spack.spec -from spack.environment.environment import SpackEnvironmentViewError, _error_on_nonempty_view_dir +from spack.environment.environment import ( + EnvironmentManifestFile, + SpackEnvironmentViewError, + _error_on_nonempty_view_dir, +) pytestmark = pytest.mark.not_on_windows("Envs are not supported on windows") @@ -623,3 +627,66 @@ def test_requires_on_virtual_and_potential_providers( assert mpileaks.satisfies("^mpich2") assert mpileaks["mpi"].satisfies("mpich2") assert not mpileaks.satisfies(f"^{possible_mpi_spec}") + + +@pytest.mark.regression("39387") +@pytest.mark.parametrize( + "spec_str", ["mpileaks +opt", "mpileaks +opt ~shared", "mpileaks ~shared +opt"] +) +def test_manifest_file_removal_works_if_spec_is_not_normalized(tmp_path, spec_str): + """Tests that we can remove a spec from a manifest file even if its string + representation is not normalized. + """ + manifest = tmp_path / "spack.yaml" + manifest.write_text( + f"""\ +spack: + specs: + - {spec_str} +""" + ) + s = spack.spec.Spec(spec_str) + spack_yaml = EnvironmentManifestFile(tmp_path) + # Doing a round trip str -> Spec -> str normalizes the representation + spack_yaml.remove_user_spec(str(s)) + spack_yaml.flush() + + assert spec_str not in manifest.read_text() + + +@pytest.mark.regression("39387") +@pytest.mark.parametrize( + "duplicate_specs,expected_number", + [ + # Swap variants, versions, etc. add spaces + (["foo +bar ~baz", "foo ~baz +bar"], 3), + (["foo @1.0 ~baz %gcc", "foo ~baz @1.0%gcc"], 3), + # Item 1 and 3 are exactly the same + (["zlib +shared", "zlib +shared", "zlib +shared"], 4), + ], +) +def test_removing_spec_from_manifest_with_exact_duplicates( + duplicate_specs, expected_number, tmp_path +): + """Tests that we can remove exact duplicates from a manifest file. + + Note that we can't get in a state with duplicates using only CLI, but this might happen + on user edited spack.yaml files. + """ + manifest = tmp_path / "spack.yaml" + manifest.write_text( + f"""\ + spack: + specs: [{", ".join(duplicate_specs)} , "zlib"] + """ + ) + + with ev.Environment(tmp_path) as env: + assert len(env.user_specs) == expected_number + env.remove(duplicate_specs[0]) + env.write() + + assert "+shared" not in manifest.read_text() + assert "zlib" in manifest.read_text() + with ev.Environment(tmp_path) as env: + assert len(env.user_specs) == 1 diff --git a/lib/spack/spack/util/crypto.py b/lib/spack/spack/util/crypto.py index df8102352e9..8eebcc92bc3 100644 --- a/lib/spack/spack/util/crypto.py +++ b/lib/spack/spack/util/crypto.py @@ -9,7 +9,8 @@ import llnl.util.tty as tty #: Set of hash algorithms that Spack can use, mapped to digest size in bytes -hashes = {"md5": 16, "sha1": 20, "sha224": 28, "sha256": 32, "sha384": 48, "sha512": 64} +hashes = {"sha256": 32, "md5": 16, "sha1": 20, "sha224": 28, "sha384": 48, "sha512": 64} +# Note: keys are ordered by popularity for earliest return in ``hash_key in version_dict`` checks. #: size of hash digests in bytes, mapped to algoritm names diff --git a/share/spack/gitlab/cloud_pipelines/.gitlab-ci.yml b/share/spack/gitlab/cloud_pipelines/.gitlab-ci.yml index 358d3af1215..45300eb7378 100644 --- a/share/spack/gitlab/cloud_pipelines/.gitlab-ci.yml +++ b/share/spack/gitlab/cloud_pipelines/.gitlab-ci.yml @@ -136,6 +136,8 @@ default: variables: KUBERNETES_CPU_REQUEST: 4000m KUBERNETES_MEMORY_REQUEST: 16G + # avoid moving targets like branches and tags + SPACK_CONCRETIZER_REQUIRE_CHECKSUM: 1 interruptible: true timeout: 60 minutes retry: diff --git a/var/spack/repos/builtin/packages/dla-future/package.py b/var/spack/repos/builtin/packages/dla-future/package.py index 57d0befe32b..3aeda901030 100644 --- a/var/spack/repos/builtin/packages/dla-future/package.py +++ b/var/spack/repos/builtin/packages/dla-future/package.py @@ -14,20 +14,36 @@ class DlaFuture(CMakePackage, CudaPackage, ROCmPackage): git = "https://github.com/eth-cscs/DLA-Future.git" maintainers = ["rasolca", "albestro", "msimberg", "aurianer"] + version("0.2.0", sha256="da73cbd1b88287c86d84b1045a05406b742be924e65c52588bbff200abd81a10") version("0.1.0", sha256="f7ffcde22edabb3dc24a624e2888f98829ee526da384cd752b2b271c731ca9b1") version("master", branch="master") variant("shared", default=True, description="Build shared libraries.") + variant( + "hdf5", + default=False, + when="@0.2.0:", + description="HDF5 support for dealing with matrices on disk.", + ) + variant("doc", default=False, description="Build documentation.") variant("miniapps", default=False, description="Build miniapps.") + variant( + "scalapack", + default=False, + when="@0.2.0:", + description="Build C API compatible with ScaLAPACK", + ) + depends_on("cmake@3.22:", type="build") depends_on("doxygen", type="build", when="+doc") depends_on("mpi") depends_on("blaspp@2022.05.00:") depends_on("lapackpp@2022.05.00:") + depends_on("scalapack", when="+scalapack") depends_on("umpire~examples") depends_on("umpire~cuda", when="~cuda") @@ -36,7 +52,8 @@ class DlaFuture(CMakePackage, CudaPackage, ROCmPackage): depends_on("umpire+rocm~shared", when="+rocm") depends_on("umpire@4.1.0:") - depends_on("pika@0.15.1:") + depends_on("pika@0.15.1:", when="@0.1") + depends_on("pika@0.16:", when="@0.2.0:") depends_on("pika-algorithms@0.1:") depends_on("pika +mpi") depends_on("pika +cuda", when="+cuda") @@ -52,6 +69,8 @@ class DlaFuture(CMakePackage, CudaPackage, ROCmPackage): depends_on("rocsolver", when="+rocm") depends_on("rocthrust", when="+rocm") + depends_on("hdf5 +cxx+mpi+threadsafe+shared", when="+hdf5") + conflicts("+cuda", when="+rocm") with when("+rocm"): @@ -92,16 +111,29 @@ def cmake_args(self): "openmp": "omp", "tbb": "tbb", } # Map MKL variants to LAPACK target name + mkl_threads = vmap[spec["intel-mkl"].variants["threads"].value] # TODO: Generalise for intel-oneapi-mkl args += [ self.define("DLAF_WITH_MKL", True), - self.define( - "MKL_LAPACK_TARGET", - "mkl::mkl_intel_32bit_{0}_dyn".format( - vmap[spec["intel-mkl"].variants["threads"].value] - ), - ), + self.define("MKL_LAPACK_TARGET", f"mkl::mkl_intel_32bit_{mkl_threads}_dyn"), ] + if "+scalapack" in spec: + if ( + "^mpich" in spec + or "^cray-mpich" in spec + or "^intel-mpi" in spec + or "^mvapich" in spec + or "^mvapich2" in spec + ): + mkl_mpi = "mpich" + elif "^openmpi" in spec: + mkl_mpi = "ompi" + args.append( + self.define( + "MKL_SCALAPACK_TARGET", + f"mkl::scalapack_{mkl_mpi}_intel_32bit_{mkl_threads}_dyn", + ) + ) else: args.append(self.define("DLAF_WITH_MKL", False)) args.append( @@ -110,6 +142,11 @@ def cmake_args(self): " ".join([spec[dep].libs.ld_flags for dep in ["blas", "lapack"]]), ) ) + if "+scalapack" in spec: + args.append(self.define("SCALAPACK_LIBRARY", spec["scalapack"].libs.ld_flags)) + + if "+scalapack" in spec: + args.append(self.define_from_variant("DLAF_WITH_SCALAPACK", "scalapack")) # CUDA/HIP args.append(self.define_from_variant("DLAF_WITH_CUDA", "cuda")) @@ -125,6 +162,9 @@ def cmake_args(self): arch_str = ";".join(archs) args.append(self.define("CMAKE_CUDA_ARCHITECTURES", arch_str)) + # HDF5 support + args.append(self.define_from_variant("DLAF_WITH_HDF5", "hdf5")) + # DOC args.append(self.define_from_variant("DLAF_BUILD_DOC", "doc")) diff --git a/var/spack/repos/builtin/packages/llvm/package.py b/var/spack/repos/builtin/packages/llvm/package.py index 16ab6177f71..a4a89f17bc6 100644 --- a/var/spack/repos/builtin/packages/llvm/package.py +++ b/var/spack/repos/builtin/packages/llvm/package.py @@ -756,7 +756,6 @@ def cmake_args(self): cmake_args = [ define("LLVM_REQUIRES_RTTI", True), define("LLVM_ENABLE_RTTI", True), - define("LLVM_ENABLE_EH", True), define("LLVM_ENABLE_LIBXML2", False), define("CLANG_DEFAULT_OPENMP_RUNTIME", "libomp"), define("PYTHON_EXECUTABLE", python.command.path), @@ -765,6 +764,16 @@ def cmake_args(self): from_variant("LLVM_ENABLE_ZSTD", "zstd"), ] + # Flang does not support exceptions from core llvm. + # LLVM_ENABLE_EH=True when building flang will soon + # fail (with changes at the llvm-project level). + # Only enable exceptions in LLVM if we are *not* + # building flang. FYI: LLVM <= 16.x will build flang + # successfully but the executable will suffer from + # link errors looking for C++ EH support. + if "+flang" not in spec: + cmake_args.append(define("LLVM_ENABLE_EH", True)) + version_suffix = spec.variants["version_suffix"].value if version_suffix != "none": cmake_args.append(define("LLVM_VERSION_SUFFIX", version_suffix)) diff --git a/var/spack/repos/builtin/packages/nvhpc/package.py b/var/spack/repos/builtin/packages/nvhpc/package.py index cd52b8a6346..7579afd8734 100644 --- a/var/spack/repos/builtin/packages/nvhpc/package.py +++ b/var/spack/repos/builtin/packages/nvhpc/package.py @@ -355,6 +355,9 @@ class Nvhpc(Package): ) variant("lapack", default=True, description="Enable LAPACK") variant("mpi", default=False, description="Enable MPI") + variant( + "default_cuda", default="default", description="Default CUDA version, for example 11.8" + ) provides("blas", when="+blas") provides("lapack", when="+lapack") @@ -373,6 +376,8 @@ def setup_build_environment(self, env): env.set("NVHPC_SILENT", "true") env.set("NVHPC_ACCEPT_EULA", "accept") env.set("NVHPC_INSTALL_DIR", self.prefix) + if self.spec.variants["default_cuda"].value != "default": + env.set("NVHPC_DEFAULT_CUDA", self.spec.variants["default_cuda"].value) if self.spec.variants["install_type"].value == "network": local_dir = join_path(self._version_prefix(), "share_objects") diff --git a/var/spack/repos/builtin/packages/py-llvmlite/package.py b/var/spack/repos/builtin/packages/py-llvmlite/package.py index b4a5ad65736..a6f7cd8a4db 100644 --- a/var/spack/repos/builtin/packages/py-llvmlite/package.py +++ b/var/spack/repos/builtin/packages/py-llvmlite/package.py @@ -26,19 +26,18 @@ class PyLlvmlite(PythonPackage): version("0.27.1", sha256="48a1c3ae69fd8920cba153bfed8a46ac46474bc706a2100226df4abffe0000ab") version("0.26.0", sha256="13e84fe6ebb0667233074b429fd44955f309dead3161ec89d9169145dbad2ebf") version("0.25.0", sha256="fd64def9a51dd7dc61913a7a08eeba5b9785522740bec5a7c5995b2a90525025") - version("0.23.0", sha256="bc8b1b46274d05b578fe9e980a6d98fa71c8727f6f9ed31d4d8468dce7aa5762") depends_on("py-setuptools", type="build") - depends_on("python@3.8:3.11", type=("build", "run"), when="@0.40.0:") - depends_on("python@3.7:3.10", type=("build", "run"), when="@0.38.0:0.39") - depends_on("python@3.7:3.9", type=("build", "run"), when="@0.37") - depends_on("python@3.6:", type=("build", "run"), when="@0.33:") - depends_on("python@2.6:2.8,3.4:", type=("build", "run")) + depends_on("python@3.8:3.11", when="@0.40:", type=("build", "run")) + depends_on("python@:3.10", when="@0.38:0.39", type=("build", "run")) + depends_on("python@:3.9", when="@0.36:0.37", type=("build", "run")) + depends_on("python@:3.8", when="@0.31:0.35", type=("build", "run")) + depends_on("python@:3.7", when="@:0.30", type=("build", "run")) - # llvmlite compatibility information taken from https://github.com/numba/llvmlite#compatibility - depends_on("llvm@14:~flang", when="@0.41:") - depends_on("llvm@11:14~flang", when="@0.40") - depends_on("llvm@11~flang", when="@0.37.0:0.39") + # https://github.com/numba/llvmlite#compatibility + depends_on("llvm@14", when="@0.41:") + depends_on("llvm@11:14", when="@0.40") + depends_on("llvm@11", when="@0.37:0.39") for t in [ "arm:", "ppc:", @@ -50,12 +49,12 @@ class PyLlvmlite(PythonPackage): "x86:", "x86_64:", ]: - depends_on("llvm@10.0.0:10.0~flang", when="@0.34.0:0.36 target={0}".format(t)) - depends_on("llvm@9.0.0:9.0~flang", when="@0.34.0:0.36 target=aarch64:") - depends_on("llvm@9.0.0:9.0~flang", when="@0.33.0:0.33") - depends_on("llvm@7.0.0:8.0~flang", when="@0.29.0:0.32") - depends_on("llvm@7.0.0:7.0~flang", when="@0.27.0:0.28") - depends_on("llvm@6.0.0:6.0~flang", when="@0.23.0:0.26") + depends_on("llvm@10.0", when=f"@0.34:0.36 target={t}") + depends_on("llvm@9.0", when="@0.34:0.36 target=aarch64:") + depends_on("llvm@9.0", when="@0.33") + depends_on("llvm@7.0:7.1,8.0", when="@0.29:0.32") + depends_on("llvm@7.0", when="@0.27:0.28") + depends_on("llvm@6.0", when="@0.23:0.26") depends_on("binutils", type="build") def setup_build_environment(self, env): diff --git a/var/spack/repos/builtin/packages/py-pandas/package.py b/var/spack/repos/builtin/packages/py-pandas/package.py index 1a46b9a9c66..7af6d95e82f 100644 --- a/var/spack/repos/builtin/packages/py-pandas/package.py +++ b/var/spack/repos/builtin/packages/py-pandas/package.py @@ -17,6 +17,7 @@ class PyPandas(PythonPackage): maintainers("adamjstewart") + version("2.1.0", sha256="62c24c7fc59e42b775ce0679cfa7b14a5f9bfb7643cfbe708c960699e05fb918") version("2.0.3", sha256="c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c") version("2.0.2", sha256="dd5476b6c3fe410ee95926873f377b856dbc4e81a9c605a0dc05aaccc6a7c6c6") version("2.0.1", sha256="19b8e5270da32b41ebf12f0e7165efa7024492e9513fb46fb631c5022ae5709d") @@ -61,70 +62,74 @@ class PyPandas(PythonPackage): version("0.24.2", sha256="4f919f409c433577a501e023943e582c57355d50a724c589e78bc1d551a535a2") version("0.24.1", sha256="435821cb2501eabbcee7e83614bd710940dc0cf28b5afbc4bdb816c31cec71af") version("0.23.4", sha256="5b24ca47acf69222e82530e89111dd9d14f9b970ab2cd3a1c2c78f0c4fbba4f4") - version("0.21.1", sha256="c5f5cba88bf0659554c41c909e1f78139f6fce8fa9315a29a23692b38ff9788a") - version("0.20.0", sha256="54f7a2bb2a7832c0446ad51d779806f07ec4ea2bb7c9aea4b83669fa97e778c4") - version("0.19.2", sha256="6f0f4f598c2b16746803c8bafef7c721c57e4844da752d36240c0acf97658014") - version("0.19.0", sha256="4697606cdf023c6b7fcb74e48aaf25cf282a1a00e339d2d274cf1b663748805b") - version("0.18.0", sha256="c975710ce8154b50f39a46aa3ea88d95b680191d1d9d4b5dd91eae7215e01814") - version("0.16.1", sha256="570d243f8cb068bf780461b9225d2e7bef7c90aa10d43cf908fe541fc92df8b6") - version("0.16.0", sha256="4013de6f8796ca9d2871218861823bd9878a8dfacd26e08ccf9afdd01bbad9f1") # Required dependencies # https://pandas.pydata.org/pandas-docs/stable/getting_started/install.html#python-version-support - depends_on("python@3.8:", type=("build", "run"), when="@1.4:") - depends_on("python@3.7.1:", type=("build", "run"), when="@1.2:") - depends_on("python@3.6.1:", type=("build", "run"), when="@1:") - depends_on("python@3.5.3:", type=("build", "run"), when="@0.25:") + depends_on("python@3.9:3.11", when="@2.1:", type=("build", "run")) + depends_on("python@3.8:3.11", when="@1.5:2.0", type=("build", "run")) + depends_on("python@3.8:3.10", when="@1.4", type=("build", "run")) + depends_on("python@:3.10", when="@1.3.3:1.3", type=("build", "run")) + depends_on("python@:3.9", when="@1.1.3:1.3.2", type=("build", "run")) + depends_on("python@:3.8", when="@0.25.2:1.1.2", type=("build", "run")) + depends_on("python@:3.7", when="@:0.25.1", type=("build", "run")) # pyproject.toml - depends_on("py-setuptools@61:", type="build", when="@2:") - depends_on("py-setuptools@51:", type="build", when="@1.3.2:") - depends_on("py-setuptools@38.6:", type="build", when="@1.3:") - depends_on("py-setuptools@24.2:", type="build") - depends_on("py-cython@0.29.33:2", type="build", when="@2:") - depends_on("py-cython@0.29.32:2", type="build", when="@1.4.4:") - depends_on("py-cython@0.29.30:2", type="build", when="@1.4.3:") - depends_on("py-cython@0.29.24:2", type="build", when="@1.3.4:") - depends_on("py-cython@0.29.21:2", type="build", when="@1.1.3:") - depends_on("py-cython@0.29.16:2", type="build", when="@1.1:") - depends_on("py-cython@0.29.13:2", type="build", when="@1:") - depends_on("py-versioneer+toml", type="build", when="@2:") + depends_on("py-meson-python@0.13.1", when="@2.1:", type="build") + depends_on("meson@1.0.1", when="@2.1:", type="build") + depends_on("py-cython@0.29.33:2", when="@2:", type="build") + depends_on("py-cython@0.29.32:2", when="@1.4.4:", type="build") + depends_on("py-cython@0.29.30:2", when="@1.4.3:", type="build") + depends_on("py-cython@0.29.24:2", when="@1.3.4:", type="build") + depends_on("py-cython@0.29.21:2", when="@1.1.3:", type="build") + depends_on("py-cython@0.29.16:2", when="@1.1:", type="build") + depends_on("py-cython@0.29.13:2", when="@1:", type="build") + depends_on("py-versioneer+toml", when="@2:", type="build") # https://pandas.pydata.org/pandas-docs/stable/getting_started/install.html#dependencies - depends_on("py-numpy@1.20.3:", type=("build", "run"), when="@1.5:") - depends_on("py-numpy@1.18.5:", type=("build", "run"), when="@1.4:") - depends_on("py-numpy@1.17.3:", type=("build", "run"), when="@1.3:") - depends_on("py-numpy@1.16.5:", type=("build", "run"), when="@1.2:") - depends_on("py-numpy@1.15.4:", type=("build", "run"), when="@1.1:") - depends_on("py-numpy@1.13.3:", type=("build", "run"), when="@0.25:") + depends_on("py-numpy@1.22.4:", when="@2.1:", type=("build", "run")) + depends_on("py-numpy@1.20.3:", when="@1.5:", type=("build", "run")) + depends_on("py-numpy@1.18.5:", when="@1.4:", type=("build", "run")) + depends_on("py-numpy@1.17.3:", when="@1.3:", type=("build", "run")) + depends_on("py-numpy@1.16.5:", when="@1.2:", type=("build", "run")) + depends_on("py-numpy@1.15.4:", when="@1.1:", type=("build", "run")) + depends_on("py-numpy@1.13.3:", when="@0.25:", type=("build", "run")) depends_on("py-numpy", type=("build", "run")) # 'NUMPY_IMPORT_ARRAY_RETVAL' was removed in numpy@1.19 - depends_on("py-numpy@:1.18", type=("build", "run"), when="@:0.25") - depends_on("py-python-dateutil@2.8.2:", type=("build", "run"), when="@2:") - depends_on("py-python-dateutil@2.8.1:", type=("build", "run"), when="@1.4:") - depends_on("py-python-dateutil@2.7.3:", type=("build", "run"), when="@1.1:") - depends_on("py-python-dateutil@2.6.1:", type=("build", "run"), when="@0.25:") + depends_on("py-numpy@:1.18", when="@:0.25", type=("build", "run")) + depends_on("py-python-dateutil@2.8.2:", when="@2:", type=("build", "run")) + depends_on("py-python-dateutil@2.8.1:", when="@1.4:", type=("build", "run")) + depends_on("py-python-dateutil@2.7.3:", when="@1.1:", type=("build", "run")) + depends_on("py-python-dateutil@2.6.1:", when="@0.25:", type=("build", "run")) depends_on("py-python-dateutil", type=("build", "run")) - depends_on("py-pytz@2020.1:", type=("build", "run"), when="@1.4:") - depends_on("py-pytz@2017.3:", type=("build", "run"), when="@1.2:") + depends_on("py-pytz@2020.1:", when="@1.4:", type=("build", "run")) + depends_on("py-pytz@2017.3:", when="@1.2:", type=("build", "run")) depends_on("py-pytz@2017.2:", type=("build", "run")) - depends_on("py-tzdata@2022.1:", type=("build", "run"), when="@2:") + depends_on("py-tzdata@2022.1:", when="@2:", type=("build", "run")) # Recommended dependencies - # https://pandas.pydata.org/pandas-docs/stable/getting_started/install.html#recommended-dependencies - depends_on("py-numexpr@2.7.3:", type=("build", "run"), when="@1.5:") - depends_on("py-numexpr@2.7.1:", type=("build", "run"), when="@1.4:") - depends_on("py-numexpr@2.7.0:", type=("build", "run"), when="@1.3:") - depends_on("py-numexpr@2.6.8:", type=("build", "run"), when="@1.2:") - depends_on("py-numexpr@2.6.2:", type=("build", "run"), when="@0.25:") + # https://pandas.pydata.org/pandas-docs/stable/getting_started/install.html#performance-dependencies-recommended + depends_on("py-numexpr@2.8.0:", when="@2.1:", type=("build", "run")) + depends_on("py-numexpr@2.7.3:", when="@1.5:", type=("build", "run")) + depends_on("py-numexpr@2.7.1:", when="@1.4:", type=("build", "run")) + depends_on("py-numexpr@2.7.0:", when="@1.3:", type=("build", "run")) + depends_on("py-numexpr@2.6.8:", when="@1.2:", type=("build", "run")) + depends_on("py-numexpr@2.6.2:", when="@0.25:", type=("build", "run")) depends_on("py-numexpr", type=("build", "run")) - depends_on("py-bottleneck@1.3.2:", type=("build", "run"), when="@1.5:") - depends_on("py-bottleneck@1.3.1:", type=("build", "run"), when="@1.4:") - depends_on("py-bottleneck@1.2.1:", type=("build", "run"), when="@0.25:") + depends_on("py-bottleneck@1.3.4:", when="@2.1:", type=("build", "run")) + depends_on("py-bottleneck@1.3.2:", when="@1.5:", type=("build", "run")) + depends_on("py-bottleneck@1.3.1:", when="@1.4:", type=("build", "run")) + depends_on("py-bottleneck@1.2.1:", when="@0.25:", type=("build", "run")) depends_on("py-bottleneck", type=("build", "run")) - depends_on("py-numba@0.53.1:", type=("build", "run"), when="@2:") + depends_on("py-numba@0.55.2:", when="@2.1:", type=("build", "run")) + depends_on("py-numba@0.53.1:", when="@2.0:", type=("build", "run")) # Optional dependencies # https://pandas.pydata.org/pandas-docs/stable/getting_started/install.html#optional-dependencies + # Historical dependencies + depends_on("py-setuptools@61:", when="@2.0", type="build") + depends_on("py-setuptools@51:", when="@1.3.2:1", type="build") + depends_on("py-setuptools@38.6:", when="@1.3.0:1.3.1", type="build") + depends_on("py-setuptools@24.2:", when="@:1.2", type="build") + skip_modules = ["pandas.tests", "pandas.plotting._matplotlib", "pandas.core._numba.kernels"]