Improve version, version range, and version list syntax and behavior (#36273)

## Version types, parsing and printing

- The version classes have changed: `VersionBase` is removed, there is now a
  `ConcreteVersion` base class. `StandardVersion` and `GitVersion` both inherit
  from this.

- The public api (`Version`, `VersionRange`, `ver`) has changed a bit:
  1. `Version` produces either `StandardVersion` or `GitVersion` instances.
  2. `VersionRange` produces a `ClosedOpenRange`, but this shouldn't affect the user.
  3. `ver` produces any of `VersionList`, `ClosedOpenRange`, `StandardVersion`
     or `GitVersion`.

- No unexpected type promotion, so that the following is no longer an identity:
  `Version(x) != VersionRange(x, x)`.

- `VersionList.concrete` now returns a version if it contains only a single element
  subtyping `ConcreteVersion` (i.e. `StandardVersion(...)` or `GitVersion(...)`)

- In version lists, the parser turns `@x` into `VersionRange(x, x)` instead
  of `Version(x)`.

- The above also means that `ver("x")` produces a range, whereas
  `ver("=x")` produces a `StandardVersion`. The `=` is part of _VersionList_
  syntax.

- `VersionList.__str__` now outputs `=x.y.z` for specific version entries,
  and `x.y.z` as a short-hand for ranges `x.y.z:x.y.z`.

- `Spec.format` no longer aliases `{version}` to `{versions}`, but pulls the
  concrete version out of the list and prints that -- except when the list is
  is not concrete, then is falls back to `{versions}` to avoid a pedantic error.
  For projections of concrete specs, `{version}` should be used to render
  `1.2.3` instead of `=1.2.3` (which you would get with `{versions}`).
  The default `Spec` format string used in `Spec.__str__` now uses
  `{versions}` so that `str(Spec(string)) == string` holds.

## Changes to `GitVersion`

- `GitVersion` is a small wrapper around `StandardVersion` which enriches it
   with a git ref. It no longer inherits from it.

- `GitVersion` _always_ needs to be able to look up an associated Spack version
  if it was not assigned (yet). It throws a `VersionLookupError` whenever `ref_version`
  is accessed but it has no means to look up the ref; in the past Spack would
  not error and use the commit sha as a literal version, which was incorrect.
   
- `GitVersion` is never equal to `StandardVersion`, nor is satisfied by it. This
  is such that we don't lose transitivity. This fixes the following bug on `develop`
  where `git_version_a == standard_version == git_version_b` does not imply
  `git_version_a == git_version_b`. It also ensures equality always implies equal
  hash, which is also currently broken on develop; inclusion tests of a set of
  versions + git versions would behave differently from inclusion tests of a
  list of the same objects.

- The above means `ver("ref=1.2.3) != ver("=1.2.3")` could break packages that branch
  on specific versions, but that was brittle already, since the same happens with
  externals: `pkg@1.2.3-external` suffixes wouldn't be exactly equal either. Instead,
  those checks should be `x.satisfies("@1.2.3")` which works both for git versions and
  custom version suffixes.

- `GitVersion` from commit will now print as `<hash>=<version>` once the
  git ref is resolved to a spack version. This is for reliability -- version is frozen
  when added to the database and queried later. It also improves performance
  since there is no need to clone all repos of all git versions after `spack clean -m`
  is run and something queries the database, triggering version comparison, such
  as potentially reuse concretization.

- The "empty VerstionStrComponent trick" for `GitVerison` is dropped since it wasn't
  representable as a version string (by design). Instead, it's replaced by `git`,
  so you get `1.2.3.git.4` (which reads 4 commits after a tag 1.2.3). This means
  that there's an edge case for version schemes `1.1.1`, `1.1.1a`, since the
  generated git version `1.1.1.git.1` (1 commit after `1.1.1`) compares larger
  than `1.1.1a`, since `a < git` are compared as strings. This is currently a
  wont-fix edge case, but if really required, could be fixed by special casing
  the `git` string.

- Saved, concrete specs (database, lock file, ...) that only had a git sha as their
  version, but have no means to look the effective Spack version anymore, will
  now see their version mapped to `hash=develop`. Previously these specs
  would always have their sha literally interpreted as a version string (even when
  it _could_ be looked up). This only applies to databases, lock files and spec.json
  files created before Spack 0.20; after this PR, we always have a Spack version
  associated to the relevant GitVersion).

- Fixes a bug where previously `to_dict` / `from_dict` (de)serialization would not
  reattach the repo to the GitVersion, causing the git hash to be used as a literal
  (bogus) version instead of the resolved version. This was in particularly breaking
  version comparison in the build process on macOS/Windows.


## Installing or matching specific versions

- In the past, `spack install pkg@3.2` would install `pkg@=3.2` if it was a
  known specific version defined in the package, even when newer patch releases
  `3.2.1`, `3.2.2`, `...` were available. This behavior was only there because
  there was no syntax to distinguish between `3.2` and `3.2.1`. Since there is
  syntax for this now through `pkg@=3.2`, the old exact matching behavior is
  removed. This means that `spack install pkg@3.2` constrains the `pkg` version
  to the range `3.2`, and `spack install pkg@=3.2` constrains it to the specific
  version `3.2`.

- Also in directives such as `depends_on("pkg@2.3")` and their when
  conditions `conflicts("...", when="@2.3")` ranges are ranges, and specific
  version matches require `@=2.3.`.

- No matching version: in the case `pkg@3.2` matches nothing, concretization
  errors. However, if you run `spack install pkg@=3.2` and this version
  doesn't exist, Spack will define it; this allows you to install non-registered
  versions.

- For consistency, you can now do `%gcc@10` and let it match a configured
  `10.x.y` compiler. It errors when there is no matching compiler.
  In the past it was interpreted like a specific `gcc@=10` version, which
  would get bootstrapped.

- When compiler _bootstrapping_ is enabled, `%gcc@=10.2.0` can be used to
  bootstrap a specific compiler version.

## Other changes

- Externals, compilers, and develop spec definitions are backwards compatible.
  They are typically defined as `pkg@3.2.1` even though they should be
  saying `pkg@=3.2.1`. Spack now transforms `pkg@3` into `pkg@=3` in those cases.

- Finally, fix strictness of `version(...)` directive/declaration. It just does a simple
  type check, and now requires strings/integers. Floats are not allowed because
  they are ambiguous `str(3.10) == "3.1"`.
This commit is contained in:
Harmen Stoppels 2023-05-06 06:04:41 +02:00 committed by GitHub
parent f6497972b8
commit fa7719a031
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
91 changed files with 2059 additions and 1870 deletions

View file

@ -1103,16 +1103,31 @@ Below are more details about the specifiers that you can add to specs.
Version specifier Version specifier
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
A version specifier comes somewhere after a package name and starts A version specifier ``pkg@<specifier>`` comes after a package name
with ``@``. It can be a single version, e.g. ``@1.0``, ``@3``, or and starts with ``@``. It can be something abstract that matches
``@1.2a7``. Or, it can be a range of versions, such as ``@1.0:1.5`` multiple known versions, or a specific version. During concretization,
(all versions between ``1.0`` and ``1.5``, inclusive). Version ranges Spack will pick the optimal version within the spec's constraints
can be open, e.g. ``:3`` means any version up to and including ``3``. according to policies set for the particular Spack installation.
This would include ``3.4`` and ``3.4.2``. ``4.2:`` means any version
above and including ``4.2``. Finally, a version specifier can be a The version specifier can be *a specific version*, such as ``@=1.0.0`` or
set of arbitrary versions, such as ``@1.0,1.5,1.7`` (``1.0``, ``1.5``, ``@=1.2a7``. Or, it can be *a range of versions*, such as ``@1.0:1.5``.
or ``1.7``). When you supply such a specifier to ``spack install``, Version ranges are inclusive, so this example includes both ``1.0``
it constrains the set of versions that Spack will install. and any ``1.5.x`` version. Version ranges can be unbounded, e.g. ``@:3``
means any version up to and including ``3``. This would include ``3.4``
and ``3.4.2``. Similarly, ``@4.2:`` means any version above and including
``4.2``. As a short-hand, ``@3`` is equivalent to the range ``@3:3`` and
includes any version with major version ``3``.
Notice that you can distinguish between the specific version ``@=3.2`` and
the range ``@3.2``. This is useful for packages that follow a versioning
scheme that omits the zero patch version number: ``3.2``, ``3.2.1``,
``3.2.2``, etc. In general it is preferable to use the range syntax
``@3.2``, since ranges also match versions with one-off suffixes, such as
``3.2-custom``.
A version specifier can also be a list of ranges and specific versions,
separated by commas. For example, ``@1.0:1.5,=1.7.1`` matches any version
in the range ``1.0:1.5`` and the specific version ``1.7.1``.
For packages with a ``git`` attribute, ``git`` references For packages with a ``git`` attribute, ``git`` references
may be specified instead of a numerical version i.e. branches, tags may be specified instead of a numerical version i.e. branches, tags
@ -1121,36 +1136,35 @@ reference provided. Acceptable syntaxes for this are:
.. code-block:: sh .. code-block:: sh
# branches and tags
foo@git.develop # use the develop branch
foo@git.0.19 # use the 0.19 tag
# commit hashes # commit hashes
foo@abcdef1234abcdef1234abcdef1234abcdef1234 # 40 character hashes are automatically treated as git commits foo@abcdef1234abcdef1234abcdef1234abcdef1234 # 40 character hashes are automatically treated as git commits
foo@git.abcdef1234abcdef1234abcdef1234abcdef1234 foo@git.abcdef1234abcdef1234abcdef1234abcdef1234
Spack versions from git reference either have an associated version supplied by the user, # branches and tags
or infer a relationship to known versions from the structure of the git repository. If an foo@git.develop # use the develop branch
associated version is supplied by the user, Spack treats the git version as equivalent to that foo@git.0.19 # use the 0.19 tag
version for all version comparisons in the package logic (e.g. ``depends_on('foo', when='@1.5')``).
The associated version can be assigned with ``[git ref]=[version]`` syntax, with the caveat that the specified version is known to Spack from either the package definition, or in the configuration preferences (i.e. ``packages.yaml``). Spack always needs to associate a Spack version with the git reference,
which is used for version comparison. This Spack version is heuristically
taken from the closest valid git tag among ancestors of the git ref.
Once a Spack version is associated with a git ref, it always printed with
the git ref. For example, if the commit ``@git.abcdefg`` is tagged
``0.19``, then the spec will be shown as ``@git.abcdefg=0.19``.
If the git ref is not exactly a tag, then the distance to the nearest tag
is also part of the resolved version. ``@git.abcdefg=0.19.git.8`` means
that the commit is 8 commits away from the ``0.19`` tag.
In cases where Spack cannot resolve a sensible version from a git ref,
users can specify the Spack version to use for the git ref. This is done
by appending ``=`` and the Spack version to the git ref. For example:
.. code-block:: sh .. code-block:: sh
foo@git.my_ref=3.2 # use the my_ref tag or branch, but treat it as version 3.2 for version comparisons foo@git.my_ref=3.2 # use the my_ref tag or branch, but treat it as version 3.2 for version comparisons
foo@git.abcdef1234abcdef1234abcdef1234abcdef1234=develop # use the given commit, but treat it as develop for version comparisons foo@git.abcdef1234abcdef1234abcdef1234abcdef1234=develop # use the given commit, but treat it as develop for version comparisons
If an associated version is not supplied then the tags in the git repo are used to determine
the most recent previous version known to Spack. Details about how versions are compared
and how Spack determines if one version is less than another are discussed in the developer guide.
If the version spec is not provided, then Spack will choose one
according to policies set for the particular spack installation. If
the spec is ambiguous, i.e. it could match multiple versions, Spack
will choose a version within the spec's constraints according to
policies set for the particular Spack installation.
Details about how versions are compared and how Spack determines if Details about how versions are compared and how Spack determines if
one version is less than another are discussed in the developer guide. one version is less than another are discussed in the developer guide.

View file

@ -215,7 +215,7 @@ def setup(sphinx):
("py:class", "spack.repo._PrependFileLoader"), ("py:class", "spack.repo._PrependFileLoader"),
("py:class", "spack.build_systems._checks.BaseBuilder"), ("py:class", "spack.build_systems._checks.BaseBuilder"),
# Spack classes that intersphinx is unable to resolve # Spack classes that intersphinx is unable to resolve
("py:class", "spack.version.VersionBase"), ("py:class", "spack.version.StandardVersion"),
("py:class", "spack.spec.DependencySpec"), ("py:class", "spack.spec.DependencySpec"),
] ]

View file

@ -851,16 +851,16 @@ Version comparison
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
Most Spack versions are numeric, a tuple of integers; for example, Most Spack versions are numeric, a tuple of integers; for example,
``apex@0.1``, ``ferret@6.96`` or ``py-netcdf@1.2.3.1``. Spack knows ``0.1``, ``6.96`` or ``1.2.3.1``. Spack knows how to compare and sort
how to compare and sort numeric versions. numeric versions.
Some Spack versions involve slight extensions of numeric syntax; for Some Spack versions involve slight extensions of numeric syntax; for
example, ``py-sphinx-rtd-theme@0.1.10a0``. In this case, numbers are example, ``py-sphinx-rtd-theme@=0.1.10a0``. In this case, numbers are
always considered to be "newer" than letters. This is for consistency always considered to be "newer" than letters. This is for consistency
with `RPM <https://bugzilla.redhat.com/show_bug.cgi?id=50977>`_. with `RPM <https://bugzilla.redhat.com/show_bug.cgi?id=50977>`_.
Spack versions may also be arbitrary non-numeric strings, for example Spack versions may also be arbitrary non-numeric strings, for example
``@develop``, ``@master``, ``@local``. ``develop``, ``master``, ``local``.
The order on versions is defined as follows. A version string is split The order on versions is defined as follows. A version string is split
into a list of components based on delimiters such as ``.``, ``-`` etc. into a list of components based on delimiters such as ``.``, ``-`` etc.
@ -918,6 +918,32 @@ use:
will be used. will be used.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ranges versus specific versions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When specifying versions in Spack using the ``pkg@<specifier>`` syntax,
you can use either ranges or specific versions. It is generally
recommended to use ranges instead of specific versions when packaging
to avoid overly constraining dependencies, patches, and conflicts.
For example, ``depends_on("python@3")`` denotes a range of versions,
allowing Spack to pick any ``3.x.y`` version for Python, while
``depends_on("python@=3.10.1")`` restricts it to a specific version.
Specific ``@=`` versions should only be used in exceptional cases, such
as when the package has a versioning scheme that omits the zero in the
first patch release: ``3.1``, ``3.1.1``, ``3.1.2``. In this example,
the specifier ``@=3.1`` is the correct way to select only the ``3.1``
version, whereas ``@3.1`` would match all those versions.
Ranges are preferred even if they would only match a single version
defined in the package. This is because users can define custom versions
in ``packages.yaml`` that typically include a custom suffix. For example,
if the package defines the version ``1.2.3``, the specifier ``@1.2.3``
will also match a user-defined version ``1.2.3-custom``.
.. _cmd-spack-checksum: .. _cmd-spack-checksum:
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
@ -2388,21 +2414,29 @@ requires Python 2, you can similarly leave out the lower bound:
Notice that we didn't use ``@:3``. Version ranges are *inclusive*, so Notice that we didn't use ``@:3``. Version ranges are *inclusive*, so
``@:3`` means "up to and including any 3.x version". ``@:3`` means "up to and including any 3.x version".
What if a package can only be built with Python 2.7? You might be You can also simply write
inclined to use:
.. code-block:: python .. code-block:: python
depends_on("python@2.7") depends_on("python@2.7")
However, this would be wrong. Spack assumes that all version constraints to tell Spack that the package needs Python 2.7.x. This is equivalent to
are exact, so it would try to install Python not at ``2.7.18``, but ``@2.7:2.7``.
exactly at ``2.7``, which is a non-existent version. The correct way to
specify this would be: In very rare cases, you may need to specify an exact version, for example
if you need to distinguish between ``3.2`` and ``3.2.1``:
.. code-block:: python .. code-block:: python
depends_on("python@2.7.0:2.7") depends_on("pkg@=3.2")
But in general, you should try to use version ranges as much as possible,
so that custom suffixes are included too. The above example can be
rewritten in terms of ranges as follows:
.. code-block:: python
depends_on("pkg@3.2:3.2.0")
A spec can contain a version list of ranges and individual versions A spec can contain a version list of ranges and individual versions
separated by commas. For example, if you need Boost 1.59.0 or newer, separated by commas. For example, if you need Boost 1.59.0 or newer,

View file

@ -776,11 +776,10 @@ def tarball_directory_name(spec):
Return name of the tarball directory according to the convention Return name of the tarball directory according to the convention
<os>-<architecture>/<compiler>/<package>-<version>/ <os>-<architecture>/<compiler>/<package>-<version>/
""" """
return "%s/%s/%s-%s" % ( return os.path.join(
spec.architecture, str(spec.architecture),
str(spec.compiler).replace("@", "-"), f"{spec.compiler.name}-{spec.compiler.version}",
spec.name, f"{spec.name}-{spec.version}",
spec.version,
) )
@ -789,13 +788,9 @@ def tarball_name(spec, ext):
Return the name of the tarfile according to the convention Return the name of the tarfile according to the convention
<os>-<architecture>-<package>-<dag_hash><ext> <os>-<architecture>-<package>-<dag_hash><ext>
""" """
return "%s-%s-%s-%s-%s%s" % ( return (
spec.architecture, f"{spec.architecture}-{spec.compiler.name}-{spec.compiler.version}-"
str(spec.compiler).replace("@", "-"), f"{spec.name}-{spec.version}-{spec.dag_hash()}{ext}"
spec.name,
spec.version,
spec.dag_hash(),
ext,
) )

View file

@ -142,7 +142,7 @@ def license_required(self):
# The Intel libraries are provided without requiring a license as of # The Intel libraries are provided without requiring a license as of
# version 2017.2. Trying to specify one anyway will fail. See: # version 2017.2. Trying to specify one anyway will fail. See:
# https://software.intel.com/en-us/articles/free-ipsxe-tools-and-libraries # https://software.intel.com/en-us/articles/free-ipsxe-tools-and-libraries
return self._has_compilers or self.version < ver("2017.2") return self._has_compilers or self.version < Version("2017.2")
#: Comment symbol used in the license.lic file #: Comment symbol used in the license.lic file
license_comment = "#" license_comment = "#"
@ -341,7 +341,7 @@ def version_yearlike(self):
v_year = year v_year = year
break break
return ver("%s.%s" % (v_year, v_tail)) return Version("%s.%s" % (v_year, v_tail))
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# Directory handling common to all Intel components # Directory handling common to all Intel components
@ -764,9 +764,9 @@ def _tbb_abi(self):
elif matches: elif matches:
# TODO: Confirm that this covers clang (needed on Linux only) # TODO: Confirm that this covers clang (needed on Linux only)
gcc_version = Version(matches.groups()[1]) gcc_version = Version(matches.groups()[1])
if gcc_version >= ver("4.7"): if gcc_version >= Version("4.7"):
abi = "gcc4.7" abi = "gcc4.7"
elif gcc_version >= ver("4.4"): elif gcc_version >= Version("4.4"):
abi = "gcc4.4" abi = "gcc4.4"
else: else:
abi = "gcc4.1" # unlikely, one hopes. abi = "gcc4.1" # unlikely, one hopes.
@ -1019,7 +1019,7 @@ def libs(self):
# Intel MPI since 2019 depends on libfabric which is not in the # Intel MPI since 2019 depends on libfabric which is not in the
# lib directory but in a directory of its own which should be # lib directory but in a directory of its own which should be
# included in the rpath # included in the rpath
if self.version_yearlike >= ver("2019"): if self.version_yearlike >= Version("2019"):
d = ancestor(self.component_lib_dir("mpi")) d = ancestor(self.component_lib_dir("mpi"))
if "+external-libfabric" in self.spec: if "+external-libfabric" in self.spec:
result += self.spec["libfabric"].libs result += self.spec["libfabric"].libs

View file

@ -290,7 +290,7 @@ def get_external_python_for_prefix(self):
python_external_config = spack.config.get("packages:python:externals", []) python_external_config = spack.config.get("packages:python:externals", [])
python_externals_configured = [ python_externals_configured = [
spack.spec.Spec(item["spec"]) spack.spec.parse_with_version_concrete(item["spec"])
for item in python_external_config for item in python_external_config
if item["prefix"] == self.spec.external_path if item["prefix"] == self.spec.external_path
] ]

View file

@ -231,7 +231,7 @@ def parse_specs(args, **kwargs):
msg += "\n\n" msg += "\n\n"
msg += unquoted_flags.report() msg += unquoted_flags.report()
raise spack.error.SpackError(msg) raise spack.error.SpackError(msg) from e
def matching_spec_from_env(spec): def matching_spec_from_env(spec):

View file

@ -19,7 +19,7 @@
from spack.package_base import deprecated_version, preferred_version from spack.package_base import deprecated_version, preferred_version
from spack.util.editor import editor from spack.util.editor import editor
from spack.util.naming import valid_fully_qualified_module_name from spack.util.naming import valid_fully_qualified_module_name
from spack.version import VersionBase, ver from spack.version import Version
description = "checksum available versions of a package" description = "checksum available versions of a package"
section = "packaging" section = "packaging"
@ -83,9 +83,10 @@ def checksum(parser, args):
pkg = pkg_cls(spack.spec.Spec(args.package)) pkg = pkg_cls(spack.spec.Spec(args.package))
url_dict = {} url_dict = {}
versions = args.versions if not args.versions and args.preferred:
if (not versions) and args.preferred:
versions = [preferred_version(pkg)] versions = [preferred_version(pkg)]
else:
versions = [Version(v) for v in args.versions]
if versions: if versions:
remote_versions = None remote_versions = None
@ -93,12 +94,6 @@ def checksum(parser, args):
if deprecated_version(pkg, version): if deprecated_version(pkg, version):
tty.warn("Version {0} is deprecated".format(version)) tty.warn("Version {0} is deprecated".format(version))
version = ver(version)
if not isinstance(version, VersionBase):
tty.die(
"Cannot generate checksums for version lists or "
"version ranges. Use unambiguous versions."
)
url = pkg.find_valid_url_for_version(version) url = pkg.find_valid_url_for_version(version)
if url is not None: if url is not None:
url_dict[version] = url url_dict[version] = url

View file

@ -107,7 +107,7 @@ def dev_build(self, args):
" Use `spack create` to create a new package", " Use `spack create` to create a new package",
) )
if not spec.versions.concrete: if not spec.versions.concrete_range_as_version:
tty.die( tty.die(
"spack dev-build spec must have a single, concrete version. " "spack dev-build spec must have a single, concrete version. "
"Did you forget a package version number?" "Did you forget a package version number?"

View file

@ -9,7 +9,9 @@
import spack.cmd import spack.cmd
import spack.cmd.common.arguments as arguments import spack.cmd.common.arguments as arguments
import spack.spec
import spack.util.path import spack.util.path
import spack.version
from spack.error import SpackError from spack.error import SpackError
description = "add a spec to an environment's dev-build information" description = "add a spec to an environment's dev-build information"
@ -61,7 +63,9 @@ def develop(parser, args):
tty.msg(msg) tty.msg(msg)
continue continue
spec = spack.spec.Spec(entry["spec"]) # Both old syntax `spack develop pkg@x` and new syntax `spack develop pkg@=x`
# are currently supported.
spec = spack.spec.parse_with_version_concrete(entry["spec"])
pkg_cls = spack.repo.path.get_pkg_class(spec.name) pkg_cls = spack.repo.path.get_pkg_class(spec.name)
pkg_cls(spec).stage.steal_source(abspath) pkg_cls(spec).stage.steal_source(abspath)
@ -75,9 +79,12 @@ def develop(parser, args):
raise SpackError("spack develop requires at most one named spec") raise SpackError("spack develop requires at most one named spec")
spec = specs[0] spec = specs[0]
if not spec.versions.concrete: version = spec.versions.concrete_range_as_version
if not version:
raise SpackError("Packages to develop must have a concrete version") raise SpackError("Packages to develop must have a concrete version")
spec.versions = spack.version.VersionList([version])
# default path is relative path to spec.name # default path is relative path to spec.name
path = args.path or spec.name path = args.path or spec.name
abspath = spack.util.path.canonicalize_path(path, default_wd=env.path) abspath = spack.util.path.canonicalize_path(path, default_wd=env.path)

View file

@ -141,7 +141,7 @@ def _process_result(result, show, required_format, kwargs):
def solve(parser, args): def solve(parser, args):
# these are the same options as `spack spec` # these are the same options as `spack spec`
name_fmt = "{namespace}.{name}" if args.namespaces else "{name}" name_fmt = "{namespace}.{name}" if args.namespaces else "{name}"
fmt = "{@version}{%compiler}{compiler_flags}{variants}{arch=architecture}" fmt = "{@versions}{%compiler}{compiler_flags}{variants}{arch=architecture}"
install_status_fn = spack.spec.Spec.install_status install_status_fn = spack.spec.Spec.install_status
kwargs = { kwargs = {
"cover": args.cover, "cover": args.cover,

View file

@ -81,7 +81,7 @@ def setup_parser(subparser):
def spec(parser, args): def spec(parser, args):
name_fmt = "{namespace}.{name}" if args.namespaces else "{name}" name_fmt = "{namespace}.{name}" if args.namespaces else "{name}"
fmt = "{@version}{%compiler}{compiler_flags}{variants}{arch=architecture}" fmt = "{@versions}{%compiler}{compiler_flags}{variants}{arch=architecture}"
install_status_fn = spack.spec.Spec.install_status install_status_fn = spack.spec.Spec.install_status
tree_kwargs = { tree_kwargs = {
"cover": args.cover, "cover": args.cover,

View file

@ -24,6 +24,7 @@
import spack.paths import spack.paths
import spack.platforms import spack.platforms
import spack.spec import spack.spec
import spack.version
from spack.util.environment import get_path from spack.util.environment import get_path
from spack.util.naming import mod_to_class from spack.util.naming import mod_to_class
@ -69,7 +70,7 @@ def pkg_spec_for_compiler(cspec):
break break
else: else:
spec_str = str(cspec) spec_str = str(cspec)
return spack.spec.Spec(spec_str) return spack.spec.parse_with_version_concrete(spec_str)
def _auto_compiler_spec(function): def _auto_compiler_spec(function):
@ -213,7 +214,7 @@ def all_compilers_config(scope=None, init_config=True):
def all_compiler_specs(scope=None, init_config=True): def all_compiler_specs(scope=None, init_config=True):
# Return compiler specs from the merged config. # Return compiler specs from the merged config.
return [ return [
spack.spec.CompilerSpec(s["compiler"]["spec"]) spack.spec.parse_with_version_concrete(s["compiler"]["spec"], compiler=True)
for s in all_compilers_config(scope, init_config) for s in all_compilers_config(scope, init_config)
] ]
@ -384,7 +385,7 @@ def __eq__(self, other):
def compiler_from_dict(items): def compiler_from_dict(items):
cspec = spack.spec.CompilerSpec(items["spec"]) cspec = spack.spec.parse_with_version_concrete(items["spec"], compiler=True)
os = items.get("operating_system", None) os = items.get("operating_system", None)
target = items.get("target", None) target = items.get("target", None)
@ -453,7 +454,10 @@ def get_compilers(config, cspec=None, arch_spec=None):
for items in config: for items in config:
items = items["compiler"] items = items["compiler"]
if cspec and items["spec"] != str(cspec):
# NOTE: in principle this should be equality not satisfies, but config can still
# be written in old format gcc@10.1.0 instead of gcc@=10.1.0.
if cspec and not cspec.satisfies(items["spec"]):
continue continue
# If an arch spec is given, confirm that this compiler # If an arch spec is given, confirm that this compiler

View file

@ -143,5 +143,5 @@ def fflags(self):
def _handle_default_flag_addtions(self): def _handle_default_flag_addtions(self):
# This is a known issue for AOCC 3.0 see: # This is a known issue for AOCC 3.0 see:
# https://developer.amd.com/wp-content/resources/AOCC-3.0-Install-Guide.pdf # https://developer.amd.com/wp-content/resources/AOCC-3.0-Install-Guide.pdf
if self.real_version == ver("3.0.0"): if self.real_version.satisfies(ver("3.0.0")):
return "-Wno-unused-command-line-argument " "-mllvm -eliminate-similar-expr=false" return "-Wno-unused-command-line-argument " "-mllvm -eliminate-similar-expr=false"

View file

@ -13,7 +13,7 @@
import spack.compiler import spack.compiler
import spack.compilers.clang import spack.compilers.clang
import spack.util.executable import spack.util.executable
import spack.version from spack.version import Version
class AppleClang(spack.compilers.clang.Clang): class AppleClang(spack.compilers.clang.Clang):
@ -41,7 +41,7 @@ def extract_version_from_output(cls, output):
@property @property
def cxx11_flag(self): def cxx11_flag(self):
# Spack's AppleClang detection only valid from Xcode >= 4.6 # Spack's AppleClang detection only valid from Xcode >= 4.6
if self.real_version < spack.version.ver("4.0"): if self.real_version < Version("4.0"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++11 standard", "cxx11_flag", "Xcode < 4.0" self, "the C++11 standard", "cxx11_flag", "Xcode < 4.0"
) )
@ -49,38 +49,38 @@ def cxx11_flag(self):
@property @property
def cxx14_flag(self): def cxx14_flag(self):
if self.real_version < spack.version.ver("5.1"): if self.real_version < Version("5.1"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++14 standard", "cxx14_flag", "Xcode < 5.1" self, "the C++14 standard", "cxx14_flag", "Xcode < 5.1"
) )
elif self.real_version < spack.version.ver("6.1"): elif self.real_version < Version("6.1"):
return "-std=c++1y" return "-std=c++1y"
return "-std=c++14" return "-std=c++14"
@property @property
def cxx17_flag(self): def cxx17_flag(self):
if self.real_version < spack.version.ver("6.1"): if self.real_version < Version("6.1"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++17 standard", "cxx17_flag", "Xcode < 6.1" self, "the C++17 standard", "cxx17_flag", "Xcode < 6.1"
) )
elif self.real_version < spack.version.ver("10.0"): elif self.real_version < Version("10.0"):
return "-std=c++1z" return "-std=c++1z"
return "-std=c++17" return "-std=c++17"
@property @property
def cxx20_flag(self): def cxx20_flag(self):
if self.real_version < spack.version.ver("10.0"): if self.real_version < Version("10.0"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++20 standard", "cxx20_flag", "Xcode < 10.0" self, "the C++20 standard", "cxx20_flag", "Xcode < 10.0"
) )
elif self.real_version < spack.version.ver("13.0"): elif self.real_version < Version("13.0"):
return "-std=c++2a" return "-std=c++2a"
return "-std=c++20" return "-std=c++20"
@property @property
def cxx23_flag(self): def cxx23_flag(self):
if self.real_version < spack.version.ver("13.0"): if self.real_version < Version("13.0"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++23 standard", "cxx23_flag", "Xcode < 13.0" self, "the C++23 standard", "cxx23_flag", "Xcode < 13.0"
) )
@ -90,7 +90,7 @@ def cxx23_flag(self):
@property @property
def c99_flag(self): def c99_flag(self):
if self.real_version < spack.version.ver("4.0"): if self.real_version < Version("4.0"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C99 standard", "c99_flag", "< 4.0" self, "the C99 standard", "c99_flag", "< 4.0"
) )
@ -98,7 +98,7 @@ def c99_flag(self):
@property @property
def c11_flag(self): def c11_flag(self):
if self.real_version < spack.version.ver("4.0"): if self.real_version < Version("4.0"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C11 standard", "c11_flag", "< 4.0" self, "the C11 standard", "c11_flag", "< 4.0"
) )
@ -106,7 +106,7 @@ def c11_flag(self):
@property @property
def c17_flag(self): def c17_flag(self):
if self.real_version < spack.version.ver("11.0"): if self.real_version < Version("11.0"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C17 standard", "c17_flag", "< 11.0" self, "the C17 standard", "c17_flag", "< 11.0"
) )
@ -114,7 +114,7 @@ def c17_flag(self):
@property @property
def c23_flag(self): def c23_flag(self):
if self.real_version < spack.version.ver("11.0.3"): if self.real_version < Version("11.0.3"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C23 standard", "c23_flag", "< 11.0.3" self, "the C23 standard", "c23_flag", "< 11.0.3"
) )

View file

@ -5,7 +5,7 @@
import os import os
from spack.compiler import Compiler, UnsupportedCompilerFlag from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import ver from spack.version import Version
class Cce(Compiler): class Cce(Compiler):
@ -58,7 +58,7 @@ def link_paths(self):
@property @property
def is_clang_based(self): def is_clang_based(self):
version = self._real_version or self.version version = self._real_version or self.version
return version >= ver("9.0") and "classic" not in str(version) return version >= Version("9.0") and "classic" not in str(version)
version_argument = "--version" version_argument = "--version"
version_regex = r"[Cc]ray (?:clang|C :|C\+\+ :|Fortran :) [Vv]ersion.*?(\d+(\.\d+)+)" version_regex = r"[Cc]ray (?:clang|C :|C\+\+ :|Fortran :) [Vv]ersion.*?(\d+(\.\d+)+)"
@ -98,9 +98,9 @@ def cxx17_flag(self):
def c99_flag(self): def c99_flag(self):
if self.is_clang_based: if self.is_clang_based:
return "-std=c99" return "-std=c99"
elif self.real_version >= ver("8.4"): elif self.real_version >= Version("8.4"):
return "-h std=c99,noconform,gnu" return "-h std=c99,noconform,gnu"
elif self.real_version >= ver("8.1"): elif self.real_version >= Version("8.1"):
return "-h c99,noconform,gnu" return "-h c99,noconform,gnu"
raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 8.1") raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 8.1")
@ -108,7 +108,7 @@ def c99_flag(self):
def c11_flag(self): def c11_flag(self):
if self.is_clang_based: if self.is_clang_based:
return "-std=c11" return "-std=c11"
elif self.real_version >= ver("8.5"): elif self.real_version >= Version("8.5"):
return "-h std=c11,noconform,gnu" return "-h std=c11,noconform,gnu"
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 8.5") raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 8.5")

View file

@ -10,7 +10,7 @@
import llnl.util.lang import llnl.util.lang
from spack.compiler import Compiler, UnsupportedCompilerFlag from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import ver from spack.version import Version
#: compiler symlink mappings for mixed f77 compilers #: compiler symlink mappings for mixed f77 compilers
f77_mapping = [ f77_mapping = [
@ -100,24 +100,24 @@ def verbose_flag(self):
@property @property
def cxx11_flag(self): def cxx11_flag(self):
if self.real_version < ver("3.3"): if self.real_version < Version("3.3"):
raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 3.3") raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 3.3")
return "-std=c++11" return "-std=c++11"
@property @property
def cxx14_flag(self): def cxx14_flag(self):
if self.real_version < ver("3.4"): if self.real_version < Version("3.4"):
raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 3.5") raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 3.5")
elif self.real_version < ver("3.5"): elif self.real_version < Version("3.5"):
return "-std=c++1y" return "-std=c++1y"
return "-std=c++14" return "-std=c++14"
@property @property
def cxx17_flag(self): def cxx17_flag(self):
if self.real_version < ver("3.5"): if self.real_version < Version("3.5"):
raise UnsupportedCompilerFlag(self, "the C++17 standard", "cxx17_flag", "< 3.5") raise UnsupportedCompilerFlag(self, "the C++17 standard", "cxx17_flag", "< 3.5")
elif self.real_version < ver("5.0"): elif self.real_version < Version("5.0"):
return "-std=c++1z" return "-std=c++1z"
return "-std=c++17" return "-std=c++17"
@ -128,21 +128,21 @@ def c99_flag(self):
@property @property
def c11_flag(self): def c11_flag(self):
if self.real_version < ver("3.0"): if self.real_version < Version("3.0"):
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 3.0") raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 3.0")
if self.real_version < ver("3.1"): if self.real_version < Version("3.1"):
return "-std=c1x" return "-std=c1x"
return "-std=c11" return "-std=c11"
@property @property
def c17_flag(self): def c17_flag(self):
if self.real_version < ver("6.0"): if self.real_version < Version("6.0"):
raise UnsupportedCompilerFlag(self, "the C17 standard", "c17_flag", "< 6.0") raise UnsupportedCompilerFlag(self, "the C17 standard", "c17_flag", "< 6.0")
return "-std=c17" return "-std=c17"
@property @property
def c23_flag(self): def c23_flag(self):
if self.real_version < ver("9.0"): if self.real_version < Version("9.0"):
raise UnsupportedCompilerFlag(self, "the C23 standard", "c23_flag", "< 9.0") raise UnsupportedCompilerFlag(self, "the C23 standard", "c23_flag", "< 9.0")
return "-std=c2x" return "-std=c2x"

View file

@ -11,7 +11,7 @@
import spack.compiler import spack.compiler
import spack.compilers.apple_clang as apple_clang import spack.compilers.apple_clang as apple_clang
import spack.util.executable import spack.util.executable
from spack.version import ver from spack.version import Version
class Gcc(spack.compiler.Compiler): class Gcc(spack.compiler.Compiler):
@ -61,47 +61,47 @@ def openmp_flag(self):
@property @property
def cxx98_flag(self): def cxx98_flag(self):
if self.real_version < ver("6.0"): if self.real_version < Version("6.0"):
return "" return ""
else: else:
return "-std=c++98" return "-std=c++98"
@property @property
def cxx11_flag(self): def cxx11_flag(self):
if self.real_version < ver("4.3"): if self.real_version < Version("4.3"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++11 standard", "cxx11_flag", " < 4.3" self, "the C++11 standard", "cxx11_flag", " < 4.3"
) )
elif self.real_version < ver("4.7"): elif self.real_version < Version("4.7"):
return "-std=c++0x" return "-std=c++0x"
else: else:
return "-std=c++11" return "-std=c++11"
@property @property
def cxx14_flag(self): def cxx14_flag(self):
if self.real_version < ver("4.8"): if self.real_version < Version("4.8"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++14 standard", "cxx14_flag", "< 4.8" self, "the C++14 standard", "cxx14_flag", "< 4.8"
) )
elif self.real_version < ver("4.9"): elif self.real_version < Version("4.9"):
return "-std=c++1y" return "-std=c++1y"
else: else:
return "-std=c++14" return "-std=c++14"
@property @property
def cxx17_flag(self): def cxx17_flag(self):
if self.real_version < ver("5.0"): if self.real_version < Version("5.0"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++17 standard", "cxx17_flag", "< 5.0" self, "the C++17 standard", "cxx17_flag", "< 5.0"
) )
elif self.real_version < ver("6.0"): elif self.real_version < Version("6.0"):
return "-std=c++1z" return "-std=c++1z"
else: else:
return "-std=c++17" return "-std=c++17"
@property @property
def c99_flag(self): def c99_flag(self):
if self.real_version < ver("4.5"): if self.real_version < Version("4.5"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C99 standard", "c99_flag", "< 4.5" self, "the C99 standard", "c99_flag", "< 4.5"
) )
@ -109,7 +109,7 @@ def c99_flag(self):
@property @property
def c11_flag(self): def c11_flag(self):
if self.real_version < ver("4.7"): if self.real_version < Version("4.7"):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C11 standard", "c11_flag", "< 4.7" self, "the C11 standard", "c11_flag", "< 4.7"
) )
@ -157,7 +157,7 @@ def default_version(cls, cc):
return "unknown" return "unknown"
version = super(Gcc, cls).default_version(cc) version = super(Gcc, cls).default_version(cc)
if ver(version) >= ver("7"): if Version(version) >= Version("7"):
output = spack.compiler.get_compiler_version_output(cc, "-dumpfullversion") output = spack.compiler.get_compiler_version_output(cc, "-dumpfullversion")
version = cls.extract_version_from_output(output) version = cls.extract_version_from_output(output)
return version return version
@ -187,7 +187,7 @@ def fc_version(cls, fc):
output = spack.compiler.get_compiler_version_output(fc, "-dumpversion") output = spack.compiler.get_compiler_version_output(fc, "-dumpversion")
match = re.search(r"(?:GNU Fortran \(GCC\) )?([\d.]+)", output) match = re.search(r"(?:GNU Fortran \(GCC\) )?([\d.]+)", output)
version = match.group(match.lastindex) if match else "unknown" version = match.group(match.lastindex) if match else "unknown"
if ver(version) >= ver("7"): if Version(version) >= Version("7"):
output = spack.compiler.get_compiler_version_output(fc, "-dumpfullversion") output = spack.compiler.get_compiler_version_output(fc, "-dumpfullversion")
version = cls.extract_version_from_output(output) version = cls.extract_version_from_output(output)
return version return version

View file

@ -7,7 +7,7 @@
import sys import sys
from spack.compiler import Compiler, UnsupportedCompilerFlag from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import ver from spack.version import Version
class Intel(Compiler): class Intel(Compiler):
@ -60,17 +60,17 @@ def opt_flags(self):
@property @property
def openmp_flag(self): def openmp_flag(self):
if self.real_version < ver("16.0"): if self.real_version < Version("16.0"):
return "-openmp" return "-openmp"
else: else:
return "-qopenmp" return "-qopenmp"
@property @property
def cxx11_flag(self): def cxx11_flag(self):
if self.real_version < ver("11.1"): if self.real_version < Version("11.1"):
raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 11.1") raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 11.1")
elif self.real_version < ver("13"): elif self.real_version < Version("13"):
return "-std=c++0x" return "-std=c++0x"
else: else:
return "-std=c++11" return "-std=c++11"
@ -78,23 +78,23 @@ def cxx11_flag(self):
@property @property
def cxx14_flag(self): def cxx14_flag(self):
# Adapted from CMake's Intel-CXX rules. # Adapted from CMake's Intel-CXX rules.
if self.real_version < ver("15"): if self.real_version < Version("15"):
raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 15") raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 15")
elif self.real_version < ver("15.0.2"): elif self.real_version < Version("15.0.2"):
return "-std=c++1y" return "-std=c++1y"
else: else:
return "-std=c++14" return "-std=c++14"
@property @property
def c99_flag(self): def c99_flag(self):
if self.real_version < ver("12"): if self.real_version < Version("12"):
raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 12") raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 12")
else: else:
return "-std=c99" return "-std=c99"
@property @property
def c11_flag(self): def c11_flag(self):
if self.real_version < ver("16"): if self.real_version < Version("16"):
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 16") raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 16")
else: else:
return "-std=c1x" return "-std=c1x"

View file

@ -6,7 +6,7 @@
import os import os
from spack.compiler import Compiler, UnsupportedCompilerFlag from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import ver from spack.version import Version
class Pgi(Compiler): class Pgi(Compiler):
@ -77,13 +77,13 @@ def fc_pic_flag(self):
@property @property
def c99_flag(self): def c99_flag(self):
if self.real_version >= ver("12.10"): if self.real_version >= Version("12.10"):
return "-c99" return "-c99"
raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 12.10") raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 12.10")
@property @property
def c11_flag(self): def c11_flag(self):
if self.real_version >= ver("15.3"): if self.real_version >= Version("15.3"):
return "-c11" return "-c11"
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 15.3") raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 15.3")

View file

@ -6,7 +6,7 @@
import os import os
from spack.compiler import Compiler, UnsupportedCompilerFlag from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import ver from spack.version import Version
class Xl(Compiler): class Xl(Compiler):
@ -51,24 +51,24 @@ def openmp_flag(self):
@property @property
def cxx11_flag(self): def cxx11_flag(self):
if self.real_version < ver("13.1"): if self.real_version < Version("13.1"):
raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 13.1") raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 13.1")
else: else:
return "-qlanglvl=extended0x" return "-qlanglvl=extended0x"
@property @property
def c99_flag(self): def c99_flag(self):
if self.real_version >= ver("13.1.1"): if self.real_version >= Version("13.1.1"):
return "-std=gnu99" return "-std=gnu99"
if self.real_version >= ver("10.1"): if self.real_version >= Version("10.1"):
return "-qlanglvl=extc99" return "-qlanglvl=extc99"
raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 10.1") raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 10.1")
@property @property
def c11_flag(self): def c11_flag(self):
if self.real_version >= ver("13.1.2"): if self.real_version >= Version("13.1.2"):
return "-std=gnu11" return "-std=gnu11"
if self.real_version >= ver("12.1"): if self.real_version >= Version("12.1"):
return "-qlanglvl=extc1x" return "-qlanglvl=extc1x"
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 12.1") raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 12.1")
@ -76,7 +76,7 @@ def c11_flag(self):
def cxx14_flag(self): def cxx14_flag(self):
# .real_version does not have the "y.z" component of "w.x.y.z", which # .real_version does not have the "y.z" component of "w.x.y.z", which
# is required to distinguish whether support is available # is required to distinguish whether support is available
if self.version >= ver("16.1.1.8"): if self.version >= Version("16.1.1.8"):
return "-std=c++14" return "-std=c++14"
raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 16.1.1.8") raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 16.1.1.8")

View file

@ -41,7 +41,7 @@
import spack.variant as vt import spack.variant as vt
from spack.config import config from spack.config import config
from spack.package_prefs import PackagePrefs, is_spec_buildable, spec_externals from spack.package_prefs import PackagePrefs, is_spec_buildable, spec_externals
from spack.version import Version, VersionList, VersionRange, ver from spack.version import ClosedOpenRange, VersionList, ver
#: impements rudimentary logic for ABI compatibility #: impements rudimentary logic for ABI compatibility
_abi: Union[spack.abi.ABI, llnl.util.lang.Singleton] = llnl.util.lang.Singleton( _abi: Union[spack.abi.ABI, llnl.util.lang.Singleton] = llnl.util.lang.Singleton(
@ -219,7 +219,7 @@ def concretize_version(self, spec):
# Respect order listed in packages.yaml # Respect order listed in packages.yaml
-yaml_prefs(v), -yaml_prefs(v),
# The preferred=True flag (packages or packages.yaml or both?) # The preferred=True flag (packages or packages.yaml or both?)
pkg_versions.get(Version(v)).get("preferred", False), pkg_versions.get(v).get("preferred", False),
# ------- Regular case: use latest non-develop version by default. # ------- Regular case: use latest non-develop version by default.
# Avoid @develop version, which would otherwise be the "largest" # Avoid @develop version, which would otherwise be the "largest"
# in straight version comparisons # in straight version comparisons
@ -246,11 +246,12 @@ def concretize_version(self, spec):
raise NoValidVersionError(spec) raise NoValidVersionError(spec)
else: else:
last = spec.versions[-1] last = spec.versions[-1]
if isinstance(last, VersionRange): if isinstance(last, ClosedOpenRange):
if last.end: range_as_version = VersionList([last]).concrete_range_as_version
spec.versions = ver([last.end]) if range_as_version:
spec.versions = ver([range_as_version])
else: else:
spec.versions = ver([last.start]) raise NoValidVersionError(spec)
else: else:
spec.versions = ver([last]) spec.versions = ver([last])

View file

@ -11,7 +11,11 @@
import llnl.util.tty as tty import llnl.util.tty as tty
import spack.cmd import spack.cmd
import spack.error
import spack.hash_types as hash_types import spack.hash_types as hash_types
import spack.platforms
import spack.repo
import spack.spec
from spack.schema.cray_manifest import schema as manifest_schema from spack.schema.cray_manifest import schema as manifest_schema
#: Cray systems can store a Spack-compatible description of system #: Cray systems can store a Spack-compatible description of system
@ -74,13 +78,13 @@ def spec_from_entry(entry):
compiler_str = "" compiler_str = ""
if "compiler" in entry: if "compiler" in entry:
compiler_format = "%{name}@{version}" compiler_format = "%{name}@={version}"
compiler_str = compiler_format.format( compiler_str = compiler_format.format(
name=translated_compiler_name(entry["compiler"]["name"]), name=translated_compiler_name(entry["compiler"]["name"]),
version=entry["compiler"]["version"], version=entry["compiler"]["version"],
) )
spec_format = "{name}@{version} {compiler} {arch}" spec_format = "{name}@={version} {compiler} {arch}"
spec_str = spec_format.format( spec_str = spec_format.format(
name=entry["name"], version=entry["version"], compiler=compiler_str, arch=arch_str name=entry["name"], version=entry["version"], compiler=compiler_str, arch=arch_str
) )

View file

@ -46,10 +46,10 @@
import spack.store import spack.store
import spack.util.lock as lk import spack.util.lock as lk
import spack.util.spack_json as sjson import spack.util.spack_json as sjson
import spack.version as vn
from spack.directory_layout import DirectoryLayoutError, InconsistentInstallDirectoryError from spack.directory_layout import DirectoryLayoutError, InconsistentInstallDirectoryError
from spack.error import SpackError from spack.error import SpackError
from spack.util.crypto import bit_length from spack.util.crypto import bit_length
from spack.version import Version
# TODO: Provide an API automatically retyring a build after detecting and # TODO: Provide an API automatically retyring a build after detecting and
# TODO: clearing a failure. # TODO: clearing a failure.
@ -60,7 +60,7 @@
# DB version. This is stuck in the DB file to track changes in format. # DB version. This is stuck in the DB file to track changes in format.
# Increment by one when the database format changes. # Increment by one when the database format changes.
# Versions before 5 were not integers. # Versions before 5 were not integers.
_db_version = Version("6") _db_version = vn.Version("6")
# For any version combinations here, skip reindex when upgrading. # For any version combinations here, skip reindex when upgrading.
# Reindexing can take considerable time and is not always necessary. # Reindexing can take considerable time and is not always necessary.
@ -70,8 +70,8 @@
# only difference is that v5 can contain "deprecated_for" # only difference is that v5 can contain "deprecated_for"
# fields. So, skip the reindex for this transition. The new # fields. So, skip the reindex for this transition. The new
# version is saved to disk the first time the DB is written. # version is saved to disk the first time the DB is written.
(Version("0.9.3"), Version("5")), (vn.Version("0.9.3"), vn.Version("5")),
(Version("5"), Version("6")), (vn.Version("5"), vn.Version("6")),
] ]
# Default timeout for spack database locks in seconds or None (no timeout). # Default timeout for spack database locks in seconds or None (no timeout).
@ -105,7 +105,7 @@
def reader(version): def reader(version):
reader_cls = {Version("5"): spack.spec.SpecfileV1, Version("6"): spack.spec.SpecfileV3} reader_cls = {vn.Version("5"): spack.spec.SpecfileV1, vn.Version("6"): spack.spec.SpecfileV3}
return reader_cls[version] return reader_cls[version]
@ -694,8 +694,7 @@ def _read_spec_from_dict(self, spec_reader, hash_key, installs, hash=ht.dag_hash
spec_dict[hash.name] = hash_key spec_dict[hash.name] = hash_key
# Build spec from dict first. # Build spec from dict first.
spec = spec_reader.from_node_dict(spec_dict) return spec_reader.from_node_dict(spec_dict)
return spec
def db_for_spec_hash(self, hash_key): def db_for_spec_hash(self, hash_key):
with self.read_transaction(): with self.read_transaction():
@ -798,7 +797,7 @@ def check(cond, msg):
installs = db["installs"] installs = db["installs"]
# TODO: better version checking semantics. # TODO: better version checking semantics.
version = Version(db["version"]) version = vn.Version(db["version"])
spec_reader = reader(version) spec_reader = reader(version)
if version > _db_version: if version > _db_version:
raise InvalidDatabaseVersionError(_db_version, version) raise InvalidDatabaseVersionError(_db_version, version)
@ -816,9 +815,11 @@ def check(cond, msg):
) )
def invalid_record(hash_key, error): def invalid_record(hash_key, error):
msg = "Invalid record in Spack database: " "hash: %s, cause: %s: %s" return CorruptDatabaseError(
msg %= (hash_key, type(error).__name__, str(error)) f"Invalid record in Spack database: hash: {hash_key}, cause: "
raise CorruptDatabaseError(msg, self._index_path) f"{type(error).__name__}: {error}",
self._index_path,
)
# Build up the database in three passes: # Build up the database in three passes:
# #
@ -846,7 +847,7 @@ def invalid_record(hash_key, error):
if not spec.external and "installed" in rec and rec["installed"]: if not spec.external and "installed" in rec and rec["installed"]:
installed_prefixes.add(rec["path"]) installed_prefixes.add(rec["path"])
except Exception as e: except Exception as e:
invalid_record(hash_key, e) raise invalid_record(hash_key, e) from e
# Pass 2: Assign dependencies once all specs are created. # Pass 2: Assign dependencies once all specs are created.
for hash_key in data: for hash_key in data:
@ -855,7 +856,7 @@ def invalid_record(hash_key, error):
except MissingDependenciesError: except MissingDependenciesError:
raise raise
except Exception as e: except Exception as e:
invalid_record(hash_key, e) raise invalid_record(hash_key, e) from e
# Pass 3: Mark all specs concrete. Specs representing real # Pass 3: Mark all specs concrete. Specs representing real
# installations must be explicitly marked. # installations must be explicitly marked.

View file

@ -32,7 +32,7 @@ class OpenMpi(Package):
import functools import functools
import os.path import os.path
import re import re
from typing import List, Optional, Set from typing import List, Optional, Set, Union
import llnl.util.lang import llnl.util.lang
import llnl.util.tty.color import llnl.util.tty.color
@ -45,7 +45,13 @@ class OpenMpi(Package):
from spack.dependency import Dependency, canonical_deptype, default_deptype from spack.dependency import Dependency, canonical_deptype, default_deptype
from spack.fetch_strategy import from_kwargs from spack.fetch_strategy import from_kwargs
from spack.resource import Resource from spack.resource import Resource
from spack.version import GitVersion, Version, VersionChecksumError, VersionLookupError from spack.version import (
GitVersion,
Version,
VersionChecksumError,
VersionError,
VersionLookupError,
)
__all__ = [ __all__ = [
"DirectiveError", "DirectiveError",
@ -318,7 +324,7 @@ def remove_directives(arg):
@directive("versions") @directive("versions")
def version( def version(
ver: str, ver: Union[str, int],
# this positional argument is deprecated, use sha256=... instead # this positional argument is deprecated, use sha256=... instead
checksum: Optional[str] = None, checksum: Optional[str] = None,
*, *,
@ -362,64 +368,72 @@ def version(
The (keyword) arguments are turned into a valid fetch strategy for The (keyword) arguments are turned into a valid fetch strategy for
code packages later. See ``spack.fetch_strategy.for_package_version()``. code packages later. See ``spack.fetch_strategy.for_package_version()``.
""" """
kwargs = {
key: value
for key, value in (
("sha256", sha256),
("sha384", sha384),
("sha512", sha512),
("preferred", preferred),
("deprecated", deprecated),
("expand", expand),
("url", url),
("extension", extension),
("no_cache", no_cache),
("fetch_options", fetch_options),
("git", git),
("svn", svn),
("hg", hg),
("cvs", cvs),
("get_full_repo", get_full_repo),
("branch", branch),
("submodules", submodules),
("submodules_delete", submodules_delete),
("commit", commit),
("tag", tag),
("revision", revision),
("date", date),
("md5", md5),
("sha1", sha1),
("sha224", sha224),
("checksum", checksum),
)
if value is not None
}
return lambda pkg: _execute_version(pkg, ver, **kwargs)
def _execute_version(pkg):
if (
any((sha256, sha384, sha512, md5, sha1, sha224, checksum))
and hasattr(pkg, "has_code")
and not pkg.has_code
):
raise VersionChecksumError(
"{0}: Checksums not allowed in no-code packages "
"(see '{1}' version).".format(pkg.name, ver)
)
kwargs = { def _execute_version(pkg, ver, **kwargs):
key: value if (
for key, value in ( any(
("sha256", sha256), s in kwargs
("sha384", sha384), for s in ("sha256", "sha384", "sha512", "md5", "sha1", "sha224", "checksum")
("sha512", sha512), )
("preferred", preferred), and hasattr(pkg, "has_code")
("deprecated", deprecated), and not pkg.has_code
("expand", expand), ):
("url", url), raise VersionChecksumError(
("extension", extension), "{0}: Checksums not allowed in no-code packages "
("no_cache", no_cache), "(see '{1}' version).".format(pkg.name, ver)
("fetch_options", fetch_options), )
("git", git),
("svn", svn),
("hg", hg),
("cvs", cvs),
("get_full_repo", get_full_repo),
("branch", branch),
("submodules", submodules),
("submodules_delete", submodules_delete),
("commit", commit),
("tag", tag),
("revision", revision),
("date", date),
("md5", md5),
("sha1", sha1),
("sha224", sha224),
("checksum", checksum),
)
if value is not None
}
# Store kwargs for the package to later with a fetch_strategy. if not isinstance(ver, (int, str)):
version = Version(ver) raise VersionError(
if isinstance(version, GitVersion): f"{pkg.name}: declared version '{ver!r}' in package should be a string or int."
if git is None and not hasattr(pkg, "git"): )
msg = "Spack version directives cannot include git hashes fetched from"
msg += " URLs. Error in package '%s'\n" % pkg.name
msg += " version('%s', " % version.string
msg += ", ".join("%s='%s'" % (argname, value) for argname, value in kwargs.items())
msg += ")"
raise VersionLookupError(msg)
pkg.versions[version] = kwargs
return _execute_version # Declared versions are concrete
version = Version(ver)
if isinstance(version, GitVersion) and not hasattr(pkg, "git") and "git" not in kwargs:
args = ", ".join(f"{argname}='{value}'" for argname, value in kwargs.items())
raise VersionLookupError(
f"{pkg.name}: spack version directives cannot include git hashes fetched from URLs.\n"
f" version('{ver}', {args})"
)
# Store kwargs for the package to later with a fetch_strategy.
pkg.versions[version] = kwargs
def _depends_on(pkg, spec, when=None, type=default_deptype, patches=None): def _depends_on(pkg, spec, when=None, type=default_deptype, patches=None):

View file

@ -49,6 +49,7 @@
import spack.util.spack_json as sjson import spack.util.spack_json as sjson
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
import spack.util.url import spack.util.url
import spack.version
from spack.filesystem_view import SimpleFilesystemView, inverse_view_func_parser, view_func_parser from spack.filesystem_view import SimpleFilesystemView, inverse_view_func_parser, view_func_parser
from spack.installer import PackageInstaller from spack.installer import PackageInstaller
from spack.spec import Spec from spack.spec import Spec
@ -774,7 +775,7 @@ def __init__(self, manifest_dir: Union[str, pathlib.Path]) -> None:
self.views: Dict[str, ViewDescriptor] = {} self.views: Dict[str, ViewDescriptor] = {}
#: Specs from "spack.yaml" #: Specs from "spack.yaml"
self.spec_lists = {user_speclist_name: SpecList()} self.spec_lists: Dict[str, SpecList] = {user_speclist_name: SpecList()}
#: Dev-build specs from "spack.yaml" #: Dev-build specs from "spack.yaml"
self.dev_specs: Dict[str, Any] = {} self.dev_specs: Dict[str, Any] = {}
#: User specs from the last concretization #: User specs from the last concretization
@ -863,7 +864,7 @@ def _construct_state_from_manifest(self):
self.dev_specs = copy.deepcopy(configuration.get("develop", {})) self.dev_specs = copy.deepcopy(configuration.get("develop", {}))
for name, entry in self.dev_specs.items(): for name, entry in self.dev_specs.items():
# spec must include a concrete version # spec must include a concrete version
assert Spec(entry["spec"]).version.concrete assert Spec(entry["spec"]).versions.concrete_range_as_version
# default path is the spec name # default path is the spec name
if "path" not in entry: if "path" not in entry:
self.dev_specs[name]["path"] = name self.dev_specs[name]["path"] = name
@ -1139,21 +1140,21 @@ def add(self, user_spec, list_name=user_speclist_name):
def change_existing_spec( def change_existing_spec(
self, self,
change_spec, change_spec: Spec,
list_name=user_speclist_name, list_name: str = user_speclist_name,
match_spec=None, match_spec: Optional[Spec] = None,
allow_changing_multiple_specs=False, allow_changing_multiple_specs=False,
): ):
""" """
Find the spec identified by `match_spec` and change it to `change_spec`. Find the spec identified by `match_spec` and change it to `change_spec`.
Arguments: Arguments:
change_spec (spack.spec.Spec): defines the spec properties that change_spec: defines the spec properties that
need to be changed. This will not change attributes of the need to be changed. This will not change attributes of the
matched spec unless they conflict with `change_spec`. matched spec unless they conflict with `change_spec`.
list_name (str): identifies the spec list in the environment that list_name: identifies the spec list in the environment that
should be modified should be modified
match_spec (spack.spec.Spec): if set, this identifies the spec match_spec: if set, this identifies the spec
that should be changed. If not set, it is assumed we are that should be changed. If not set, it is assumed we are
looking for a spec with the same name as `change_spec`. looking for a spec with the same name as `change_spec`.
""" """
@ -1252,15 +1253,15 @@ def remove(self, query_spec, list_name=user_speclist_name, force=False):
del self.concretized_order[i] del self.concretized_order[i]
del self.specs_by_hash[dag_hash] del self.specs_by_hash[dag_hash]
def develop(self, spec, path, clone=False): def develop(self, spec: Spec, path: str, clone: bool = False) -> bool:
"""Add dev-build info for package """Add dev-build info for package
Args: Args:
spec (spack.spec.Spec): Set constraints on development specs. Must include a spec: Set constraints on development specs. Must include a
concrete version. concrete version.
path (str): Path to find code for developer builds. Relative path: Path to find code for developer builds. Relative
paths will be resolved relative to the environment. paths will be resolved relative to the environment.
clone (bool): Clone the package code to the path. clone: Clone the package code to the path.
If clone is False Spack will assume the code is already present If clone is False Spack will assume the code is already present
at ``path``. at ``path``.

View file

@ -874,12 +874,12 @@ def clone(self, dest=None, commit=None, branch=None, tag=None, bare=False):
# If we want a particular branch ask for it. # If we want a particular branch ask for it.
if branch: if branch:
args.extend(["--branch", branch]) args.extend(["--branch", branch])
elif tag and self.git_version >= spack.version.ver("1.8.5.2"): elif tag and self.git_version >= spack.version.Version("1.8.5.2"):
args.extend(["--branch", tag]) args.extend(["--branch", tag])
# Try to be efficient if we're using a new enough git. # Try to be efficient if we're using a new enough git.
# This checks out only one branch's history # This checks out only one branch's history
if self.git_version >= spack.version.ver("1.7.10"): if self.git_version >= spack.version.Version("1.7.10"):
if self.get_full_repo: if self.get_full_repo:
args.append("--no-single-branch") args.append("--no-single-branch")
else: else:
@ -890,7 +890,7 @@ def clone(self, dest=None, commit=None, branch=None, tag=None, bare=False):
# tree, if the in-use git and protocol permit it. # tree, if the in-use git and protocol permit it.
if ( if (
(not self.get_full_repo) (not self.get_full_repo)
and self.git_version >= spack.version.ver("1.7.1") and self.git_version >= spack.version.Version("1.7.1")
and self.protocol_supports_shallow_clone() and self.protocol_supports_shallow_clone()
): ):
args.extend(["--depth", "1"]) args.extend(["--depth", "1"])
@ -907,7 +907,7 @@ def clone(self, dest=None, commit=None, branch=None, tag=None, bare=False):
# For tags, be conservative and check them out AFTER # For tags, be conservative and check them out AFTER
# cloning. Later git versions can do this with clone # cloning. Later git versions can do this with clone
# --branch, but older ones fail. # --branch, but older ones fail.
if tag and self.git_version < spack.version.ver("1.8.5.2"): if tag and self.git_version < spack.version.Version("1.8.5.2"):
# pull --tags returns a "special" error code of 1 in # pull --tags returns a "special" error code of 1 in
# older versions that we have to ignore. # older versions that we have to ignore.
# see: https://github.com/git/git/commit/19d122b # see: https://github.com/git/git/commit/19d122b
@ -1516,7 +1516,7 @@ def for_package_version(pkg, version=None):
assert not pkg.spec.concrete, "concrete specs should not pass the 'version=' argument" assert not pkg.spec.concrete, "concrete specs should not pass the 'version=' argument"
# Specs are initialized with the universe range, if no version information is given, # Specs are initialized with the universe range, if no version information is given,
# so here we make sure we always match the version passed as argument # so here we make sure we always match the version passed as argument
if not isinstance(version, spack.version.VersionBase): if not isinstance(version, spack.version.StandardVersion):
version = spack.version.Version(version) version = spack.version.Version(version)
version_list = spack.version.VersionList() version_list = spack.version.VersionList()
@ -1529,10 +1529,10 @@ def for_package_version(pkg, version=None):
if isinstance(version, spack.version.GitVersion): if isinstance(version, spack.version.GitVersion):
if not hasattr(pkg, "git"): if not hasattr(pkg, "git"):
raise web_util.FetchError( raise web_util.FetchError(
"Cannot fetch git version for %s. Package has no 'git' attribute" % pkg.name f"Cannot fetch git version for {pkg.name}. Package has no 'git' attribute"
) )
# Populate the version with comparisons to other commits # Populate the version with comparisons to other commits
version.generate_git_lookup(pkg.name) version.attach_git_lookup_from_package(pkg.name)
# For GitVersion, we have no way to determine whether a ref is a branch or tag # For GitVersion, we have no way to determine whether a ref is a branch or tag
# Fortunately, we handle branches and tags identically, except tags are # Fortunately, we handle branches and tags identically, except tags are
@ -1545,15 +1545,11 @@ def for_package_version(pkg, version=None):
kwargs["submodules"] = getattr(pkg, "submodules", False) kwargs["submodules"] = getattr(pkg, "submodules", False)
# if we have a ref_version already, and it is a version from the package # if the ref_version is a known version from the package, use that version's
# we can use that version's submodule specifications # submodule specifications
if pkg.version.ref_version: ref_version_attributes = pkg.versions.get(pkg.version.ref_version)
ref_version = spack.version.Version(pkg.version.ref_version[0]) if ref_version_attributes:
ref_version_attributes = pkg.versions.get(ref_version) kwargs["submodules"] = ref_version_attributes.get("submodules", kwargs["submodules"])
if ref_version_attributes:
kwargs["submodules"] = ref_version_attributes.get(
"submodules", kwargs["submodules"]
)
fetcher = GitFetchStrategy(**kwargs) fetcher = GitFetchStrategy(**kwargs)
return fetcher return fetcher

View file

@ -52,6 +52,7 @@
import spack.package_base import spack.package_base
import spack.package_prefs as prefs import spack.package_prefs as prefs
import spack.repo import spack.repo
import spack.spec
import spack.store import spack.store
import spack.util.executable import spack.util.executable
import spack.util.path import spack.util.path
@ -628,9 +629,7 @@ def package_id(pkg):
derived derived
""" """
if not pkg.spec.concrete: if not pkg.spec.concrete:
raise ValueError( raise ValueError("Cannot provide a unique, readable id when the spec is not concretized.")
"Cannot provide a unique, readable id when " "the spec is not concretized."
)
return "{0}-{1}-{2}".format(pkg.name, pkg.version, pkg.spec.dag_hash()) return "{0}-{1}-{2}".format(pkg.name, pkg.version, pkg.spec.dag_hash())
@ -908,7 +907,6 @@ def _prepare_for_install(self, task):
""" """
install_args = task.request.install_args install_args = task.request.install_args
keep_prefix = install_args.get("keep_prefix") keep_prefix = install_args.get("keep_prefix")
keep_stage = install_args.get("keep_stage")
restage = install_args.get("restage") restage = install_args.get("restage")
# Make sure the package is ready to be locally installed. # Make sure the package is ready to be locally installed.
@ -941,9 +939,9 @@ def _prepare_for_install(self, task):
else: else:
tty.debug("{0} is partially installed".format(task.pkg_id)) tty.debug("{0} is partially installed".format(task.pkg_id))
# Destroy the stage for a locally installed, non-DIYStage, package # Destroy the stage for a locally installed, non-DIYStage, package
if restage and task.pkg.stage.managed_by_spack: if restage and task.pkg.stage.managed_by_spack:
task.pkg.stage.destroy() task.pkg.stage.destroy()
if installed_in_db and ( if installed_in_db and (
rec.spec.dag_hash() not in task.request.overwrite rec.spec.dag_hash() not in task.request.overwrite
@ -955,12 +953,6 @@ def _prepare_for_install(self, task):
if task.explicit: if task.explicit:
spack.store.db.update_explicit(task.pkg.spec, True) spack.store.db.update_explicit(task.pkg.spec, True)
# In case the stage directory has already been created, this
# check ensures it is removed after we checked that the spec is
# installed.
if not keep_stage:
task.pkg.stage.destroy()
def _cleanup_all_tasks(self): def _cleanup_all_tasks(self):
"""Cleanup all build tasks to include releasing their locks.""" """Cleanup all build tasks to include releasing their locks."""
for pkg_id in self.locks: for pkg_id in self.locks:

View file

@ -62,7 +62,7 @@
from spack.util.package_hash import package_hash from spack.util.package_hash import package_hash
from spack.util.prefix import Prefix from spack.util.prefix import Prefix
from spack.util.web import FetchError from spack.util.web import FetchError
from spack.version import GitVersion, Version, VersionBase from spack.version import GitVersion, StandardVersion, Version
FLAG_HANDLER_RETURN_TYPE = Tuple[ FLAG_HANDLER_RETURN_TYPE = Tuple[
Optional[Iterable[str]], Optional[Iterable[str]], Optional[Iterable[str]] Optional[Iterable[str]], Optional[Iterable[str]], Optional[Iterable[str]]
@ -97,9 +97,9 @@ def deprecated_version(pkg, version):
Arguments: Arguments:
pkg (PackageBase): The package whose version is to be checked. pkg (PackageBase): The package whose version is to be checked.
version (str or spack.version.VersionBase): The version being checked version (str or spack.version.StandardVersion): The version being checked
""" """
if not isinstance(version, VersionBase): if not isinstance(version, StandardVersion):
version = Version(version) version = Version(version)
for k, v in pkg.versions.items(): for k, v in pkg.versions.items():
@ -120,7 +120,7 @@ def preferred_version(pkg):
# as preferred in the package, then on the fact that the # as preferred in the package, then on the fact that the
# version is not develop, then lexicographically # version is not develop, then lexicographically
key_fn = lambda v: (pkg.versions[v].get("preferred", False), not v.isdevelop(), v) key_fn = lambda v: (pkg.versions[v].get("preferred", False), not v.isdevelop(), v)
return sorted(pkg.versions, key=key_fn).pop() return max(pkg.versions, key=key_fn)
class WindowsRPath(object): class WindowsRPath(object):
@ -928,7 +928,7 @@ def all_urls_for_version(self, version):
return self._implement_all_urls_for_version(version, uf) return self._implement_all_urls_for_version(version, uf)
def _implement_all_urls_for_version(self, version, custom_url_for_version=None): def _implement_all_urls_for_version(self, version, custom_url_for_version=None):
if not isinstance(version, VersionBase): if not isinstance(version, StandardVersion):
version = Version(version) version = Version(version)
urls = [] urls = []

View file

@ -9,9 +9,9 @@
import spack.repo import spack.repo
from spack.config import ConfigError from spack.config import ConfigError
from spack.util.path import canonicalize_path from spack.util.path import canonicalize_path
from spack.version import VersionList from spack.version import Version
_lesser_spec_types = {"compiler": spack.spec.CompilerSpec, "version": VersionList} _lesser_spec_types = {"compiler": spack.spec.CompilerSpec, "version": Version}
def _spec_type(component): def _spec_type(component):

View file

@ -95,7 +95,7 @@
VALUE = r"([a-zA-Z_0-9\-+\*.,:=\~\/\\]+)" VALUE = r"([a-zA-Z_0-9\-+\*.,:=\~\/\\]+)"
QUOTED_VALUE = r"[\"']+([a-zA-Z_0-9\-+\*.,:=\~\/\\\s]+)[\"']+" QUOTED_VALUE = r"[\"']+([a-zA-Z_0-9\-+\*.,:=\~\/\\\s]+)[\"']+"
VERSION = r"([a-zA-Z0-9_][a-zA-Z_0-9\-\.]*\b)" VERSION = r"=?([a-zA-Z0-9_][a-zA-Z_0-9\-\.]*\b)"
VERSION_RANGE = rf"({VERSION}\s*:\s*{VERSION}(?!\s*=)|:\s*{VERSION}(?!\s*=)|{VERSION}\s*:|:)" VERSION_RANGE = rf"({VERSION}\s*:\s*{VERSION}(?!\s*=)|:\s*{VERSION}(?!\s*=)|{VERSION}\s*:|:)"
VERSION_LIST = rf"({VERSION_RANGE}|{VERSION})(\s*[,]\s*({VERSION_RANGE}|{VERSION}))*" VERSION_LIST = rf"({VERSION_RANGE}|{VERSION})(\s*[,]\s*({VERSION_RANGE}|{VERSION}))*"
@ -361,19 +361,10 @@ def parse(self, initial_spec: spack.spec.Spec) -> spack.spec.Spec:
raise spack.spec.MultipleVersionError( raise spack.spec.MultipleVersionError(
f"{initial_spec} cannot have multiple versions" f"{initial_spec} cannot have multiple versions"
) )
initial_spec.versions = spack.version.VersionList(
version_list = spack.version.VersionList() [spack.version.from_string(self.ctx.current_token.value[1:])]
version_list.add(spack.version.from_string(self.ctx.current_token.value[1:])) )
initial_spec.versions = version_list initial_spec.attach_git_version_lookup()
# Add a git lookup method for GitVersions
if (
initial_spec.name
and initial_spec.versions.concrete
and isinstance(initial_spec.version, spack.version.GitVersion)
):
initial_spec.version.generate_git_lookup(initial_spec.fullname)
self.has_version = True self.has_version = True
elif self.ctx.accept(TokenType.BOOL_VARIANT): elif self.ctx.accept(TokenType.BOOL_VARIANT):
self.hash_not_parsed_or_raise(initial_spec, self.ctx.current_token.value) self.hash_not_parsed_or_raise(initial_spec, self.ctx.current_token.value)

View file

@ -53,7 +53,8 @@
"version": { "version": {
"type": "array", "type": "array",
"default": [], "default": [],
# version strings # version strings (type should be string, number is still possible
# but deprecated. this is to avoid issues with e.g. 3.10 -> 3.1)
"items": {"anyOf": [{"type": "string"}, {"type": "number"}]}, "items": {"anyOf": [{"type": "string"}, {"type": "number"}]},
}, },
"target": { "target": {
@ -131,3 +132,16 @@
"additionalProperties": False, "additionalProperties": False,
"properties": properties, "properties": properties,
} }
def update(data):
changed = False
for key in data:
version = data[key].get("version")
if not version or all(isinstance(v, str) for v in version):
continue
data[key]["version"] = [str(v) for v in version]
changed = True
return changed

View file

@ -43,10 +43,11 @@
import spack.repo import spack.repo
import spack.spec import spack.spec
import spack.store import spack.store
import spack.traverse
import spack.util.path import spack.util.path
import spack.util.timer import spack.util.timer
import spack.variant import spack.variant
import spack.version import spack.version as vn
# these are from clingo.ast and bootstrapped later # these are from clingo.ast and bootstrapped later
ASTType = None ASTType = None
@ -893,7 +894,7 @@ def key_fn(version):
most_to_least_preferred = [] most_to_least_preferred = []
for _, group in itertools.groupby(partially_sorted_versions, key=key_fn): for _, group in itertools.groupby(partially_sorted_versions, key=key_fn):
most_to_least_preferred.extend( most_to_least_preferred.extend(
list(sorted(group, reverse=True, key=lambda x: spack.version.ver(x.version))) list(sorted(group, reverse=True, key=lambda x: vn.ver(x.version)))
) )
for weight, declared_version in enumerate(most_to_least_preferred): for weight, declared_version in enumerate(most_to_least_preferred):
@ -920,7 +921,7 @@ def spec_versions(self, spec):
if spec.concrete: if spec.concrete:
return [fn.attr("version", spec.name, spec.version)] return [fn.attr("version", spec.name, spec.version)]
if spec.versions == spack.version.ver(":"): if spec.versions == vn.any_version:
return [] return []
# record all version constraints for later # record all version constraints for later
@ -1286,6 +1287,7 @@ def emit_facts_from_requirement_rules(self, rules, *, virtual=False, raise_on_fa
spec = spack.spec.Spec(spec_str) spec = spack.spec.Spec(spec_str)
if not spec.name: if not spec.name:
spec.name = pkg_name spec.name = pkg_name
spec.attach_git_version_lookup()
when_spec = spec when_spec = spec
if virtual: if virtual:
when_spec = spack.spec.Spec(pkg_name) when_spec = spack.spec.Spec(pkg_name)
@ -1330,7 +1332,7 @@ def external_packages(self):
# Read a list of all the specs for this package # Read a list of all the specs for this package
externals = data.get("externals", []) externals = data.get("externals", [])
external_specs = [spack.spec.Spec(x["spec"]) for x in externals] external_specs = [spack.spec.parse_with_version_concrete(x["spec"]) for x in externals]
# Order the external versions to prefer more recent versions # Order the external versions to prefer more recent versions
# even if specs in packages.yaml are not ordered that way # even if specs in packages.yaml are not ordered that way
@ -1636,48 +1638,27 @@ def key_fn(item):
version_preferences = packages_yaml.get(pkg_name, {}).get("version", []) version_preferences = packages_yaml.get(pkg_name, {}).get("version", [])
for idx, v in enumerate(version_preferences): for idx, v in enumerate(version_preferences):
# v can be a string so force it into an actual version for comparisons # v can be a string so force it into an actual version for comparisons
ver = spack.version.Version(v) ver = vn.Version(v)
self.declared_versions[pkg_name].append( self.declared_versions[pkg_name].append(
DeclaredVersion(version=ver, idx=idx, origin=version_provenance.packages_yaml) DeclaredVersion(version=ver, idx=idx, origin=version_provenance.packages_yaml)
) )
self.possible_versions[pkg_name].add(ver)
def add_concrete_versions_from_specs(self, specs, origin): def add_concrete_versions_from_specs(self, specs, origin):
"""Add concrete versions to possible versions from lists of CLI/dev specs.""" """Add concrete versions to possible versions from lists of CLI/dev specs."""
for spec in specs: for s in spack.traverse.traverse_nodes(specs):
for dep in spec.traverse(): # If there is a concrete version on the CLI *that we know nothing
if not dep.versions.concrete: # about*, add it to the known versions. Use idx=0, which is the
continue # best possible, so they're guaranteed to be used preferentially.
version = s.versions.concrete
known_versions = self.possible_versions[dep.name] if version is None or any(v == version for v in self.possible_versions[s.name]):
if not isinstance(dep.version, spack.version.GitVersion) and any( continue
v.satisfies(dep.version) for v in known_versions
):
# some version we know about satisfies this constraint, so we
# should use that one. e.g, if the user asks for qt@5 and we
# know about qt@5.5. This ensures we don't add under-specified
# versions to the solver
#
# For git versions, we know the version is already fully specified
# so we don't have to worry about whether it's an under-specified
# version
continue
# if there is a concrete version on the CLI *that we know nothing self.declared_versions[s.name].append(
# about*, add it to the known versions. Use idx=0, which is the DeclaredVersion(version=version, idx=0, origin=origin)
# best possible, so they're guaranteed to be used preferentially. )
self.declared_versions[dep.name].append( self.possible_versions[s.name].add(version)
DeclaredVersion(version=dep.version, idx=0, origin=origin)
)
self.possible_versions[dep.name].add(dep.version)
if (
isinstance(dep.version, spack.version.GitVersion)
and dep.version.user_supplied_reference
):
defined_version = spack.version.Version(dep.version.ref_version_str)
self.declared_versions[dep.name].append(
DeclaredVersion(version=defined_version, idx=1, origin=origin)
)
self.possible_versions[dep.name].add(defined_version)
def _supported_targets(self, compiler_name, compiler_version, targets): def _supported_targets(self, compiler_name, compiler_version, targets):
"""Get a list of which targets are supported by the compiler. """Get a list of which targets are supported by the compiler.
@ -1872,28 +1853,26 @@ def generate_possible_compilers(self, specs):
# add compiler specs from the input line to possibilities if we # add compiler specs from the input line to possibilities if we
# don't require compilers to exist. # don't require compilers to exist.
strict = spack.concretize.Concretizer().check_for_compiler_existence strict = spack.concretize.Concretizer().check_for_compiler_existence
for spec in specs: for s in spack.traverse.traverse_nodes(specs):
for s in spec.traverse(): # we don't need to validate compilers for already-built specs
# we don't need to validate compilers for already-built specs if s.concrete or not s.compiler:
if s.concrete: continue
continue
if not s.compiler or not s.compiler.concrete: version = s.compiler.versions.concrete
continue
if strict and s.compiler not in cspecs: if not version or any(c.satisfies(s.compiler) for c in cspecs):
if not s.concrete: continue
raise spack.concretize.UnavailableCompilerVersionError(s.compiler)
# Allow unknown compilers to exist if the associated spec # Error when a compiler is not found and strict mode is enabled
# is already built if strict:
else: raise spack.concretize.UnavailableCompilerVersionError(s.compiler)
compiler_cls = spack.compilers.class_for_compiler_name(s.compiler.name)
compilers.append( # Make up a compiler matching the input spec. This is for bootstrapping.
compiler_cls( compiler_cls = spack.compilers.class_for_compiler_name(s.compiler.name)
s.compiler, operating_system=None, target=None, paths=[None] * 4 compilers.append(
) compiler_cls(s.compiler, operating_system=None, target=None, paths=[None] * 4)
) )
self.gen.fact(fn.allow_compiler(s.compiler.name, s.compiler.version)) self.gen.fact(fn.allow_compiler(s.compiler.name, version))
return list( return list(
sorted( sorted(
@ -1906,25 +1885,9 @@ def generate_possible_compilers(self, specs):
def define_version_constraints(self): def define_version_constraints(self):
"""Define what version_satisfies(...) means in ASP logic.""" """Define what version_satisfies(...) means in ASP logic."""
for pkg_name, versions in sorted(self.version_constraints): for pkg_name, versions in sorted(self.version_constraints):
# version must be *one* of the ones the spec allows.
# Also, "possible versions" contain only concrete versions, so satisfies is appropriate
allowed_versions = [
v for v in sorted(self.possible_versions[pkg_name]) if v.satisfies(versions)
]
# This is needed to account for a variable number of
# numbers e.g. if both 1.0 and 1.0.2 are possible versions
exact_match = [
v
for v in allowed_versions
if v == versions and not isinstance(v, spack.version.GitVersion)
]
if exact_match:
allowed_versions = exact_match
# generate facts for each package constraint and the version # generate facts for each package constraint and the version
# that satisfies it # that satisfies it
for v in allowed_versions: for v in sorted(v for v in self.possible_versions[pkg_name] if v.satisfies(versions)):
self.gen.fact(fn.version_satisfies(pkg_name, versions, v)) self.gen.fact(fn.version_satisfies(pkg_name, versions, v))
self.gen.newline() self.gen.newline()
@ -1943,13 +1906,11 @@ def define_virtual_constraints(self):
# extract all the real versions mentioned in version ranges # extract all the real versions mentioned in version ranges
def versions_for(v): def versions_for(v):
if isinstance(v, spack.version.VersionBase): if isinstance(v, vn.StandardVersion):
return [v] return [v]
elif isinstance(v, spack.version.VersionRange): elif isinstance(v, vn.ClosedOpenRange):
result = [v.start] if v.start else [] return [v.lo, vn.prev_version(v.hi)]
result += [v.end] if v.end else [] elif isinstance(v, vn.VersionList):
return result
elif isinstance(v, spack.version.VersionList):
return sum((versions_for(e) for e in v), []) return sum((versions_for(e) for e in v), [])
else: else:
raise TypeError("expected version type, found: %s" % type(v)) raise TypeError("expected version type, found: %s" % type(v))
@ -2237,14 +2198,9 @@ def _specs_from_requires(pkg_name, section):
spec.name = pkg_name spec.name = pkg_name
extracted_specs.append(spec) extracted_specs.append(spec)
version_specs = [] version_specs = [x for x in extracted_specs if x.versions.concrete]
for spec in extracted_specs: for spec in version_specs:
try: spec.attach_git_version_lookup()
spec.version
version_specs.append(spec)
except spack.error.SpecError:
pass
return version_specs return version_specs
@ -2320,11 +2276,11 @@ def variant_value(self, pkg, name, value):
self._specs[pkg].update_variant_validate(name, value) self._specs[pkg].update_variant_validate(name, value)
def version(self, pkg, version): def version(self, pkg, version):
self._specs[pkg].versions = spack.version.ver([version]) self._specs[pkg].versions = vn.VersionList([vn.Version(version)])
def node_compiler_version(self, pkg, compiler, version): def node_compiler_version(self, pkg, compiler, version):
self._specs[pkg].compiler = spack.spec.CompilerSpec(compiler) self._specs[pkg].compiler = spack.spec.CompilerSpec(compiler)
self._specs[pkg].compiler.versions = spack.version.VersionList([version]) self._specs[pkg].compiler.versions = vn.VersionList([vn.Version(version)])
def node_flag_compiler_default(self, pkg): def node_flag_compiler_default(self, pkg):
self._flag_compiler_defaults.add(pkg) self._flag_compiler_defaults.add(pkg)
@ -2525,8 +2481,8 @@ def build_specs(self, function_tuples):
# concretization process) # concretization process)
for root in self._specs.values(): for root in self._specs.values():
for spec in root.traverse(): for spec in root.traverse():
if isinstance(spec.version, spack.version.GitVersion): if isinstance(spec.version, vn.GitVersion):
spec.version.generate_git_lookup(spec.fullname) spec.version.attach_git_lookup_from_package(spec.fullname)
return self._specs return self._specs

View file

@ -55,7 +55,7 @@
import os import os
import re import re
import warnings import warnings
from typing import Tuple from typing import Tuple, Union
import llnl.util.filesystem as fs import llnl.util.filesystem as fs
import llnl.util.lang as lang import llnl.util.lang as lang
@ -145,12 +145,8 @@
#: ``color_formats.keys()``. #: ``color_formats.keys()``.
_separators = "[\\%s]" % "\\".join(color_formats.keys()) _separators = "[\\%s]" % "\\".join(color_formats.keys())
#: Versionlist constant so we don't have to build a list default_format = "{name}{@versions}"
#: every time we call str() default_format += "{%compiler.name}{@compiler.versions}{compiler_flags}"
_any_version = vn.VersionList([":"])
default_format = "{name}{@version}"
default_format += "{%compiler.name}{@compiler.version}{compiler_flags}"
default_format += "{variants}{arch=architecture}" default_format += "{variants}{arch=architecture}"
#: Regular expression to pull spec contents out of clearsigned signature #: Regular expression to pull spec contents out of clearsigned signature
@ -581,20 +577,18 @@ def __init__(self, *args):
elif nargs == 2: elif nargs == 2:
name, version = args name, version = args
self.name = name self.name = name
self.versions = vn.VersionList() self.versions = vn.VersionList([vn.ver(version)])
versions = vn.ver(version)
self.versions.add(versions)
else: else:
raise TypeError("__init__ takes 1 or 2 arguments. (%d given)" % nargs) raise TypeError("__init__ takes 1 or 2 arguments. (%d given)" % nargs)
def _add_versions(self, version_list): def _add_versions(self, version_list):
# If it already has a non-trivial version list, this is an error # If it already has a non-trivial version list, this is an error
if self.versions and self.versions != vn.VersionList(":"): if self.versions and self.versions != vn.any_version:
# Note: This may be impossible to reach by the current parser # Note: This may be impossible to reach by the current parser
# Keeping it in case the implementation changes. # Keeping it in case the implementation changes.
raise MultipleVersionError( raise MultipleVersionError(
"A spec cannot contain multiple version signifiers." " Use a version list instead." "A spec cannot contain multiple version signifiers. Use a version list instead."
) )
self.versions = vn.VersionList() self.versions = vn.VersionList()
for version in version_list: for version in version_list:
@ -677,9 +671,8 @@ def from_dict(d):
def __str__(self): def __str__(self):
out = self.name out = self.name
if self.versions and self.versions != _any_version: if self.versions and self.versions != vn.any_version:
vlist = ",".join(str(v) for v in self.versions) out += f"@{self.versions}"
out += "@%s" % vlist
return out return out
def __repr__(self): def __repr__(self):
@ -1477,7 +1470,7 @@ def _dependencies_dict(self, deptype="all"):
def _add_versions(self, version_list): def _add_versions(self, version_list):
"""Called by the parser to add an allowable version.""" """Called by the parser to add an allowable version."""
# If it already has a non-trivial version list, this is an error # If it already has a non-trivial version list, this is an error
if self.versions and self.versions != vn.VersionList(":"): if self.versions and self.versions != vn.any_version:
raise MultipleVersionError( raise MultipleVersionError(
"A spec cannot contain multiple version signifiers." " Use a version list instead." "A spec cannot contain multiple version signifiers." " Use a version list instead."
) )
@ -2108,7 +2101,7 @@ def override(init_spec, change_spec):
# (and the user spec) have dependencies # (and the user spec) have dependencies
new_spec = init_spec.copy() new_spec = init_spec.copy()
package_cls = spack.repo.path.get_pkg_class(new_spec.name) package_cls = spack.repo.path.get_pkg_class(new_spec.name)
if change_spec.versions and not change_spec.versions == spack.version.ver(":"): if change_spec.versions and not change_spec.versions == vn.any_version:
new_spec.versions = change_spec.versions new_spec.versions = change_spec.versions
for variant, value in change_spec.variants.items(): for variant, value in change_spec.variants.items():
if variant in package_cls.variants: if variant in package_cls.variants:
@ -2289,12 +2282,17 @@ def from_dict(data):
""" """
# Legacy specfile format # Legacy specfile format
if isinstance(data["spec"], list): if isinstance(data["spec"], list):
return SpecfileV1.load(data) spec = SpecfileV1.load(data)
elif int(data["spec"]["_meta"]["version"]) == 2:
spec = SpecfileV2.load(data)
else:
spec = SpecfileV3.load(data)
specfile_version = int(data["spec"]["_meta"]["version"]) # Any git version should
if specfile_version == 2: for s in spec.traverse():
return SpecfileV2.load(data) s.attach_git_version_lookup()
return SpecfileV3.load(data)
return spec
@staticmethod @staticmethod
def from_yaml(stream): def from_yaml(stream):
@ -2823,6 +2821,26 @@ def _mark_root_concrete(self, value=True):
return return
self._normal = value self._normal = value
self._concrete = value self._concrete = value
self._validate_version()
def _validate_version(self):
# Specs that were concretized with just a git sha as version, without associated
# Spack version, get their Spack version mapped to develop. This should only apply
# when reading specs concretized with Spack 0.19 or earlier. Currently Spack always
# ensures that GitVersion specs have an associated Spack version.
v = self.versions.concrete
if not isinstance(v, vn.GitVersion):
return
try:
v.ref_version
except vn.VersionLookupError:
before = self.cformat("{name}{@version}{/hash:7}")
v._ref_version = vn.StandardVersion.from_string("develop")
tty.debug(
f"the git sha of {before} could not be resolved to spack version; "
f"it has been replaced by {self.cformat('{name}{@version}{/hash:7}')}."
)
def _mark_concrete(self, value=True): def _mark_concrete(self, value=True):
"""Mark this spec and its dependencies as concrete. """Mark this spec and its dependencies as concrete.
@ -4166,9 +4184,13 @@ def write_attribute(spec, attribute, color):
if part == "arch": if part == "arch":
part = "architecture" part = "architecture"
elif part == "version": elif part == "version":
# Version requires concrete spec, versions does not # version (singular) requires a concrete versions list. Avoid
# when concrete, they print the same thing # pedantic errors by using versions (plural) when not concrete.
part = "versions" # These two are not entirely equivalent for pkg@=1.2.3:
# - version prints '1.2.3'
# - versions prints '=1.2.3'
if not current.versions.concrete:
part = "versions"
try: try:
current = getattr(current, part) current = getattr(current, part)
except AttributeError: except AttributeError:
@ -4177,7 +4199,7 @@ def write_attribute(spec, attribute, color):
m += "Spec %s has no attribute %s" % (parent, part) m += "Spec %s has no attribute %s" % (parent, part)
raise SpecFormatStringError(m) raise SpecFormatStringError(m)
if isinstance(current, vn.VersionList): if isinstance(current, vn.VersionList):
if current == _any_version: if current == vn.any_version:
# We don't print empty version lists # We don't print empty version lists
return return
@ -4195,7 +4217,7 @@ def write_attribute(spec, attribute, color):
col = "=" col = "="
elif "compiler" in parts or "compiler_flags" in parts: elif "compiler" in parts or "compiler_flags" in parts:
col = "%" col = "%"
elif "version" in parts: elif "version" in parts or "versions" in parts:
col = "@" col = "@"
# Finally, write the output # Finally, write the output
@ -4539,6 +4561,23 @@ def __hash__(self):
def __reduce__(self): def __reduce__(self):
return Spec.from_dict, (self.to_dict(hash=ht.process_hash),) return Spec.from_dict, (self.to_dict(hash=ht.process_hash),)
def attach_git_version_lookup(self):
# Add a git lookup method for GitVersions
if not self.name:
return
for v in self.versions:
if isinstance(v, vn.GitVersion) and v._ref_version is None:
v.attach_git_lookup_from_package(self.fullname)
def parse_with_version_concrete(string: str, compiler: bool = False):
"""Same as Spec(string), but interprets @x as @=x"""
s: Union[CompilerSpec, Spec] = CompilerSpec(string) if compiler else Spec(string)
interpreted_version = s.versions.concrete_range_as_version
if interpreted_version:
s.versions = vn.VersionList([interpreted_version])
return s
def merge_abstract_anonymous_specs(*abstract_specs: Spec): def merge_abstract_anonymous_specs(*abstract_specs: Spec):
"""Merge the abstracts specs passed as input and return the result. """Merge the abstracts specs passed as input and return the result.
@ -4580,6 +4619,7 @@ def from_node_dict(cls, node):
if "version" in node or "versions" in node: if "version" in node or "versions" in node:
spec.versions = vn.VersionList.from_dict(node) spec.versions = vn.VersionList.from_dict(node)
spec.attach_git_version_lookup()
if "arch" in node: if "arch" in node:
spec.architecture = ArchSpec.from_dict(node) spec.architecture = ArchSpec.from_dict(node)
@ -4614,7 +4654,8 @@ def from_node_dict(cls, node):
) )
# specs read in are concrete unless marked abstract # specs read in are concrete unless marked abstract
spec._concrete = node.get("concrete", True) if node.get("concrete", True):
spec._mark_root_concrete()
if "patches" in node: if "patches" in node:
patches = node["patches"] patches = node["patches"]

View file

@ -3,6 +3,7 @@
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import itertools import itertools
from typing import List
import spack.variant import spack.variant
from spack.error import SpackError from spack.error import SpackError
@ -59,7 +60,7 @@ def specs_as_constraints(self):
return self._constraints return self._constraints
@property @property
def specs(self): def specs(self) -> List[Spec]:
if self._specs is None: if self._specs is None:
specs = [] specs = []
# This could be slightly faster done directly from yaml_list, # This could be slightly faster done directly from yaml_list,
@ -167,6 +168,9 @@ def __len__(self):
def __getitem__(self, key): def __getitem__(self, key):
return self.specs[key] return self.specs[key]
def __iter__(self):
return iter(self.specs)
def _expand_matrix_constraints(matrix_config): def _expand_matrix_constraints(matrix_config):
# recurse so we can handle nested matrices # recurse so we can handle nested matrices

View file

@ -142,24 +142,24 @@ def test_optimization_flags(compiler_spec, target_name, expected_flags, config):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"compiler,real_version,target_str,expected_flags", "compiler,real_version,target_str,expected_flags",
[ [
(spack.spec.CompilerSpec("gcc@9.2.0"), None, "haswell", "-march=haswell -mtune=haswell"), (spack.spec.CompilerSpec("gcc@=9.2.0"), None, "haswell", "-march=haswell -mtune=haswell"),
# Check that custom string versions are accepted # Check that custom string versions are accepted
( (
spack.spec.CompilerSpec("gcc@10foo"), spack.spec.CompilerSpec("gcc@=10foo"),
"9.2.0", "9.2.0",
"icelake", "icelake",
"-march=icelake-client -mtune=icelake-client", "-march=icelake-client -mtune=icelake-client",
), ),
# Check that we run version detection (4.4.0 doesn't support icelake) # Check that we run version detection (4.4.0 doesn't support icelake)
( (
spack.spec.CompilerSpec("gcc@4.4.0-special"), spack.spec.CompilerSpec("gcc@=4.4.0-special"),
"9.2.0", "9.2.0",
"icelake", "icelake",
"-march=icelake-client -mtune=icelake-client", "-march=icelake-client -mtune=icelake-client",
), ),
# Check that the special case for Apple's clang is treated correctly # Check that the special case for Apple's clang is treated correctly
# i.e. it won't try to detect the version again # i.e. it won't try to detect the version again
(spack.spec.CompilerSpec("apple-clang@9.1.0"), None, "x86_64", "-march=x86-64"), (spack.spec.CompilerSpec("apple-clang@=9.1.0"), None, "x86_64", "-march=x86-64"),
], ],
) )
def test_optimization_flags_with_custom_versions( def test_optimization_flags_with_custom_versions(

View file

@ -281,7 +281,7 @@ def test_ci_generate_bootstrap_gcc(
- bootstrap: - bootstrap:
- gcc@3.0 - gcc@3.0
specs: specs:
- dyninst%gcc@3.0 - dyninst%gcc@=3.0
mirrors: mirrors:
some-mirror: https://my.fake.mirror some-mirror: https://my.fake.mirror
ci: ci:
@ -341,7 +341,7 @@ def test_ci_generate_bootstrap_artifacts_buildcache(
- bootstrap: - bootstrap:
- gcc@3.0 - gcc@3.0
specs: specs:
- dyninst%gcc@3.0 - dyninst%gcc@=3.0
mirrors: mirrors:
some-mirror: https://my.fake.mirror some-mirror: https://my.fake.mirror
ci: ci:
@ -1527,7 +1527,7 @@ def test_ci_generate_with_workarounds(
"""\ """\
spack: spack:
specs: specs:
- callpath%gcc@9.5 - callpath%gcc@=9.5
mirrors: mirrors:
some-mirror: https://my.fake.mirror some-mirror: https://my.fake.mirror
ci: ci:
@ -1644,7 +1644,7 @@ def test_ci_generate_bootstrap_prune_dag(
mirror_url = "file://{0}".format(mirror_dir.strpath) mirror_url = "file://{0}".format(mirror_dir.strpath)
# Install a compiler, because we want to put it in a buildcache # Install a compiler, because we want to put it in a buildcache
install_cmd("gcc@12.2.0%gcc@10.2.1") install_cmd("gcc@=12.2.0%gcc@10.2.1")
# Put installed compiler in the buildcache # Put installed compiler in the buildcache
buildcache_cmd("push", "-u", "-a", "-f", "-d", mirror_dir.strpath, "gcc@12.2.0%gcc@10.2.1") buildcache_cmd("push", "-u", "-a", "-f", "-d", mirror_dir.strpath, "gcc@12.2.0%gcc@10.2.1")
@ -1654,12 +1654,12 @@ def test_ci_generate_bootstrap_prune_dag(
monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False) monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False)
spack.config.set("config:install_missing_compilers", True) spack.config.set("config:install_missing_compilers", True)
assert CompilerSpec("gcc@12.2.0") not in compilers.all_compiler_specs() assert CompilerSpec("gcc@=12.2.0") not in compilers.all_compiler_specs()
# Configure the mirror where we put that buildcache w/ the compiler # Configure the mirror where we put that buildcache w/ the compiler
mirror_cmd("add", "test-mirror", mirror_url) mirror_cmd("add", "test-mirror", mirror_url)
install_cmd("--no-check-signature", "b%gcc@12.2.0") install_cmd("--no-check-signature", "b%gcc@=12.2.0")
# Put spec built with installed compiler in the buildcache # Put spec built with installed compiler in the buildcache
buildcache_cmd("push", "-u", "-a", "-f", "-d", mirror_dir.strpath, "b%gcc@12.2.0") buildcache_cmd("push", "-u", "-a", "-f", "-d", mirror_dir.strpath, "b%gcc@12.2.0")
@ -1674,7 +1674,7 @@ def test_ci_generate_bootstrap_prune_dag(
spack: spack:
definitions: definitions:
- bootstrap: - bootstrap:
- gcc@12.2.0%gcc@10.2.1 - gcc@=12.2.0%gcc@10.2.1
specs: specs:
- b%gcc@12.2.0 - b%gcc@12.2.0
mirrors: mirrors:

View file

@ -632,13 +632,13 @@ def test_config_prefer_upstream(
# Make sure only the non-default variants are set. # Make sure only the non-default variants are set.
assert packages["boost"] == { assert packages["boost"] == {
"compiler": ["gcc@10.2.1"], "compiler": ["gcc@=10.2.1"],
"variants": "+debug +graph", "variants": "+debug +graph",
"version": ["1.63.0"], "version": ["1.63.0"],
} }
assert packages["dependency-install"] == {"compiler": ["gcc@10.2.1"], "version": ["2.0"]} assert packages["dependency-install"] == {"compiler": ["gcc@=10.2.1"], "version": ["2.0"]}
# Ensure that neither variant gets listed for hdf5, since they conflict # Ensure that neither variant gets listed for hdf5, since they conflict
assert packages["hdf5"] == {"compiler": ["gcc@10.2.1"], "version": ["2.3"]} assert packages["hdf5"] == {"compiler": ["gcc@=10.2.1"], "version": ["2.3"]}
# Make sure a message about the conflicting hdf5's was given. # Make sure a message about the conflicting hdf5's was given.
assert "- hdf5" in output assert "- hdf5" in output

View file

@ -265,7 +265,7 @@ def test_dev_build_multiple(
# without the environment, the user would need to set dev_path for both the # without the environment, the user would need to set dev_path for both the
# root and dependency if they wanted a dev build for both. # root and dependency if they wanted a dev build for both.
leaf_dir = tmpdir.mkdir("leaf") leaf_dir = tmpdir.mkdir("leaf")
leaf_spec = spack.spec.Spec("dev-build-test-install@1.0.0") leaf_spec = spack.spec.Spec("dev-build-test-install@=1.0.0") # non-existing version
leaf_pkg_cls = spack.repo.path.get_pkg_class(leaf_spec.name) leaf_pkg_cls = spack.repo.path.get_pkg_class(leaf_spec.name)
with leaf_dir.as_cwd(): with leaf_dir.as_cwd():
with open(leaf_pkg_cls.filename, "w") as f: with open(leaf_pkg_cls.filename, "w") as f:
@ -293,7 +293,7 @@ def test_dev_build_multiple(
develop: develop:
dev-build-test-install: dev-build-test-install:
path: %s path: %s
spec: dev-build-test-install@1.0.0 spec: dev-build-test-install@=1.0.0
dev-build-test-dependent: dev-build-test-dependent:
spec: dev-build-test-dependent@0.0.0 spec: dev-build-test-dependent@0.0.0
path: %s path: %s

View file

@ -48,19 +48,19 @@ def test_develop_no_path_no_clone(self):
# develop checks that the path exists # develop checks that the path exists
fs.mkdirp(os.path.join(e.path, "mpich")) fs.mkdirp(os.path.join(e.path, "mpich"))
develop("--no-clone", "mpich@1.0") develop("--no-clone", "mpich@1.0")
self.check_develop(e, spack.spec.Spec("mpich@1.0")) self.check_develop(e, spack.spec.Spec("mpich@=1.0"))
def test_develop_no_clone(self, tmpdir): def test_develop_no_clone(self, tmpdir):
env("create", "test") env("create", "test")
with ev.read("test") as e: with ev.read("test") as e:
develop("--no-clone", "-p", str(tmpdir), "mpich@1.0") develop("--no-clone", "-p", str(tmpdir), "mpich@1.0")
self.check_develop(e, spack.spec.Spec("mpich@1.0"), str(tmpdir)) self.check_develop(e, spack.spec.Spec("mpich@=1.0"), str(tmpdir))
def test_develop(self): def test_develop(self):
env("create", "test") env("create", "test")
with ev.read("test") as e: with ev.read("test") as e:
develop("mpich@1.0") develop("mpich@1.0")
self.check_develop(e, spack.spec.Spec("mpich@1.0")) self.check_develop(e, spack.spec.Spec("mpich@=1.0"))
def test_develop_no_args(self): def test_develop_no_args(self):
env("create", "test") env("create", "test")
@ -71,20 +71,20 @@ def test_develop_no_args(self):
# test develop with no args # test develop with no args
develop() develop()
self.check_develop(e, spack.spec.Spec("mpich@1.0")) self.check_develop(e, spack.spec.Spec("mpich@=1.0"))
def test_develop_twice(self): def test_develop_twice(self):
env("create", "test") env("create", "test")
with ev.read("test") as e: with ev.read("test") as e:
develop("mpich@1.0") develop("mpich@1.0")
self.check_develop(e, spack.spec.Spec("mpich@1.0")) self.check_develop(e, spack.spec.Spec("mpich@=1.0"))
develop("mpich@1.0") develop("mpich@1.0")
# disk representation isn't updated unless we write # disk representation isn't updated unless we write
# second develop command doesn't change it, so we don't write # second develop command doesn't change it, so we don't write
# but we check disk representation # but we check disk representation
e.write() e.write()
self.check_develop(e, spack.spec.Spec("mpich@1.0")) self.check_develop(e, spack.spec.Spec("mpich@=1.0"))
assert len(e.dev_specs) == 1 assert len(e.dev_specs) == 1
def test_develop_update_path(self, tmpdir): def test_develop_update_path(self, tmpdir):
@ -92,7 +92,7 @@ def test_develop_update_path(self, tmpdir):
with ev.read("test") as e: with ev.read("test") as e:
develop("mpich@1.0") develop("mpich@1.0")
develop("-p", str(tmpdir), "mpich@1.0") develop("-p", str(tmpdir), "mpich@1.0")
self.check_develop(e, spack.spec.Spec("mpich@1.0"), str(tmpdir)) self.check_develop(e, spack.spec.Spec("mpich@=1.0"), str(tmpdir))
assert len(e.dev_specs) == 1 assert len(e.dev_specs) == 1
def test_develop_update_spec(self): def test_develop_update_spec(self):
@ -100,7 +100,7 @@ def test_develop_update_spec(self):
with ev.read("test") as e: with ev.read("test") as e:
develop("mpich@1.0") develop("mpich@1.0")
develop("mpich@2.0") develop("mpich@2.0")
self.check_develop(e, spack.spec.Spec("mpich@2.0")) self.check_develop(e, spack.spec.Spec("mpich@=2.0"))
assert len(e.dev_specs) == 1 assert len(e.dev_specs) == 1
def test_develop_canonicalize_path(self, monkeypatch, config): def test_develop_canonicalize_path(self, monkeypatch, config):
@ -115,7 +115,7 @@ def check_path(stage, dest):
monkeypatch.setattr(spack.stage.Stage, "steal_source", check_path) monkeypatch.setattr(spack.stage.Stage, "steal_source", check_path)
develop("-p", path, "mpich@1.0") develop("-p", path, "mpich@1.0")
self.check_develop(e, spack.spec.Spec("mpich@1.0"), path) self.check_develop(e, spack.spec.Spec("mpich@=1.0"), path)
# Check modifications actually worked # Check modifications actually worked
assert spack.spec.Spec("mpich@1.0").concretized().satisfies("dev_path=%s" % abspath) assert spack.spec.Spec("mpich@1.0").concretized().satisfies("dev_path=%s" % abspath)
@ -142,7 +142,7 @@ def check_path(stage, dest):
os.rmdir(abspath) os.rmdir(abspath)
develop() develop()
self.check_develop(e, spack.spec.Spec("mpich@1.0"), path) self.check_develop(e, spack.spec.Spec("mpich@=1.0"), path)
# Check modifications actually worked # Check modifications actually worked
assert spack.spec.Spec("mpich@1.0").concretized().satisfies("dev_path=%s" % abspath) assert spack.spec.Spec("mpich@1.0").concretized().satisfies("dev_path=%s" % abspath)

View file

@ -691,7 +691,7 @@ def test_env_with_config(environment_from_manifest):
- mpileaks - mpileaks
packages: packages:
mpileaks: mpileaks:
version: [2.2] version: ["2.2"]
""" """
) )
with e: with e:
@ -741,7 +741,7 @@ def test_env_with_include_config_files_same_basename(environment_from_manifest):
"""\ """\
packages: packages:
libelf: libelf:
version: [0.8.10] version: ["0.8.10"]
""" """
) )
@ -751,7 +751,7 @@ def test_env_with_include_config_files_same_basename(environment_from_manifest):
"""\ """\
packages: packages:
mpileaks: mpileaks:
version: [2.2] version: ["2.2"]
""" """
) )
@ -770,7 +770,7 @@ def packages_file(tmpdir):
raw_yaml = """ raw_yaml = """
packages: packages:
mpileaks: mpileaks:
version: [2.2] version: ["2.2"]
""" """
filename = tmpdir.ensure("testconfig", "packages.yaml") filename = tmpdir.ensure("testconfig", "packages.yaml")
filename.write(raw_yaml) filename.write(raw_yaml)
@ -829,7 +829,7 @@ def test_env_with_included_config_file_url(tmpdir, mutable_empty_config, package
assert len(scopes) == 1 assert len(scopes) == 1
cfg = spack.config.get("packages") cfg = spack.config.get("packages")
assert cfg["mpileaks"]["version"] == [2.2] assert cfg["mpileaks"]["version"] == ["2.2"]
def test_env_with_included_config_missing_file(tmpdir, mutable_empty_config): def test_env_with_included_config_missing_file(tmpdir, mutable_empty_config):
@ -895,7 +895,7 @@ def test_env_config_precedence(environment_from_manifest):
spack: spack:
packages: packages:
libelf: libelf:
version: [0.8.12] version: ["0.8.12"]
include: include:
- ./included-config.yaml - ./included-config.yaml
specs: specs:
@ -907,9 +907,9 @@ def test_env_config_precedence(environment_from_manifest):
"""\ """\
packages: packages:
mpileaks: mpileaks:
version: [2.2] version: ["2.2"]
libelf: libelf:
version: [0.8.11] version: ["0.8.11"]
""" """
) )
@ -940,7 +940,7 @@ def test_included_config_precedence(environment_from_manifest):
"""\ """\
packages: packages:
libelf: libelf:
version: [0.8.10] # this should override libelf version below version: ["0.8.10"] # this should override libelf version below
""" """
) )
@ -949,9 +949,9 @@ def test_included_config_precedence(environment_from_manifest):
"""\ """\
packages: packages:
mpileaks: mpileaks:
version: [2.2] version: ["2.2"]
libelf: libelf:
version: [0.8.12] version: ["0.8.12"]
""" """
) )
@ -2647,11 +2647,11 @@ def test_custom_version_concretize_together(tmpdir):
e.unify = True e.unify = True
# Concretize a first time using 'mpich' as the MPI provider # Concretize a first time using 'mpich' as the MPI provider
e.add("hdf5@myversion") e.add("hdf5@=myversion")
e.add("mpich") e.add("mpich")
e.concretize() e.concretize()
assert any("hdf5@myversion" in spec for _, spec in e.concretized_specs()) assert any(spec.satisfies("hdf5@myversion") for _, spec in e.concretized_specs())
def test_modules_relative_to_views(environment_from_manifest, install_mockery, mock_fetch): def test_modules_relative_to_views(environment_from_manifest, install_mockery, mock_fetch):
@ -2751,7 +2751,7 @@ def test_query_develop_specs():
with ev.read("test") as e: with ev.read("test") as e:
e.add("mpich") e.add("mpich")
e.add("mpileaks") e.add("mpileaks")
e.develop(Spec("mpich@1"), "here", clone=False) e.develop(Spec("mpich@=1"), "here", clone=False)
assert e.is_develop(Spec("mpich")) assert e.is_develop(Spec("mpich"))
assert not e.is_develop(Spec("mpileaks")) assert not e.is_develop(Spec("mpileaks"))

View file

@ -30,7 +30,7 @@ def test_fetch_single_spec(tmpdir, mock_archive, mock_stage, mock_fetch, install
@pytest.mark.disable_clean_stage_check @pytest.mark.disable_clean_stage_check
def test_fetch_multiple_specs(tmpdir, mock_archive, mock_stage, mock_fetch, install_mockery): def test_fetch_multiple_specs(tmpdir, mock_archive, mock_stage, mock_fetch, install_mockery):
SpackCommand("fetch")("mpileaks", "gcc@10.2.0", "python") SpackCommand("fetch")("mpileaks", "gcc@3.0", "python")
def test_fetch_no_argument(): def test_fetch_no_argument():

View file

@ -265,10 +265,7 @@ def test_install_commit(mock_git_version_info, install_mockery, mock_packages, m
) )
# Use the earliest commit in the respository # Use the earliest commit in the respository
commit = commits[-1] spec = Spec(f"git-test-commit@{commits[-1]}").concretized()
spec = spack.spec.Spec("git-test-commit@%s" % commit)
spec.concretize()
print(spec)
spec.package.do_install() spec.package.do_install()
# Ensure first commit file contents were written # Ensure first commit file contents were written
@ -942,10 +939,10 @@ def test_compiler_bootstrap(
): ):
monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False) monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False)
spack.config.set("config:install_missing_compilers", True) spack.config.set("config:install_missing_compilers", True)
assert CompilerSpec("gcc@12.0") not in compilers.all_compiler_specs() assert CompilerSpec("gcc@=12.0") not in compilers.all_compiler_specs()
# Test succeeds if it does not raise an error # Test succeeds if it does not raise an error
install("a%gcc@12.0") install("a%gcc@=12.0")
def test_compiler_bootstrap_from_binary_mirror( def test_compiler_bootstrap_from_binary_mirror(
@ -966,7 +963,7 @@ def test_compiler_bootstrap_from_binary_mirror(
mirror_url = "file://{0}".format(mirror_dir.strpath) mirror_url = "file://{0}".format(mirror_dir.strpath)
# Install a compiler, because we want to put it in a buildcache # Install a compiler, because we want to put it in a buildcache
install("gcc@10.2.0") install("gcc@=10.2.0")
# Put installed compiler in the buildcache # Put installed compiler in the buildcache
buildcache("push", "-u", "-a", "-f", "-d", mirror_dir.strpath, "gcc@10.2.0") buildcache("push", "-u", "-a", "-f", "-d", mirror_dir.strpath, "gcc@10.2.0")
@ -976,7 +973,7 @@ def test_compiler_bootstrap_from_binary_mirror(
monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False) monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False)
spack.config.set("config:install_missing_compilers", True) spack.config.set("config:install_missing_compilers", True)
assert CompilerSpec("gcc@10.2.0") not in compilers.all_compiler_specs() assert CompilerSpec("gcc@=10.2.0") not in compilers.all_compiler_specs()
# Configure the mirror where we put that buildcache w/ the compiler # Configure the mirror where we put that buildcache w/ the compiler
mirror("add", "test-mirror", mirror_url) mirror("add", "test-mirror", mirror_url)
@ -984,7 +981,7 @@ def test_compiler_bootstrap_from_binary_mirror(
# Now make sure that when the compiler is installed from binary mirror, # Now make sure that when the compiler is installed from binary mirror,
# it also gets configured as a compiler. Test succeeds if it does not # it also gets configured as a compiler. Test succeeds if it does not
# raise an error # raise an error
install("--no-check-signature", "--cache-only", "--only", "dependencies", "b%gcc@10.2.0") install("--no-check-signature", "--cache-only", "--only", "dependencies", "b%gcc@=10.2.0")
install("--no-cache", "--only", "package", "b%gcc@10.2.0") install("--no-cache", "--only", "package", "b%gcc@10.2.0")
@ -1000,11 +997,11 @@ def test_compiler_bootstrap_already_installed(
monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False) monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False)
spack.config.set("config:install_missing_compilers", True) spack.config.set("config:install_missing_compilers", True)
assert CompilerSpec("gcc@12.0") not in compilers.all_compiler_specs() assert CompilerSpec("gcc@=12.0") not in compilers.all_compiler_specs()
# Test succeeds if it does not raise an error # Test succeeds if it does not raise an error
install("gcc@12.0") install("gcc@=12.0")
install("a%gcc@12.0") install("a%gcc@=12.0")
def test_install_fails_no_args(tmpdir): def test_install_fails_no_args(tmpdir):

View file

@ -24,12 +24,12 @@
def test_spec(): def test_spec():
output = spec("mpileaks") output = spec("mpileaks")
assert "mpileaks@2.3" in output assert "mpileaks@=2.3" in output
assert "callpath@1.0" in output assert "callpath@=1.0" in output
assert "dyninst@8.2" in output assert "dyninst@=8.2" in output
assert "libdwarf@20130729" in output assert "libdwarf@=20130729" in output
assert "libelf@0.8.1" in output assert "libelf@=0.8.1" in output
assert "mpich@3.0.4" in output assert "mpich@=3.0.4" in output
def test_spec_concretizer_args(mutable_config, mutable_database): def test_spec_concretizer_args(mutable_config, mutable_database):
@ -196,12 +196,12 @@ def test_env_aware_spec(mutable_mock_env_path):
with env: with env:
output = spec() output = spec()
assert "mpileaks@2.3" in output assert "mpileaks@=2.3" in output
assert "callpath@1.0" in output assert "callpath@=1.0" in output
assert "dyninst@8.2" in output assert "dyninst@=8.2" in output
assert "libdwarf@20130729" in output assert "libdwarf@=20130729" in output
assert "libelf@0.8.1" in output assert "libelf@=0.8.1" in output
assert "mpich@3.0.4" in output assert "mpich@=3.0.4" in output
@pytest.mark.parametrize( @pytest.mark.parametrize(

View file

@ -89,7 +89,7 @@ def fake_stage(pkg, mirror_only=False):
monkeypatch.setattr(spack.package_base.PackageBase, "do_stage", fake_stage) monkeypatch.setattr(spack.package_base.PackageBase, "do_stage", fake_stage)
e = ev.create("test") e = ev.create("test")
e.add("mpileaks@100.100") e.add("mpileaks@=100.100")
e.concretize() e.concretize()
with e: with e:
@ -101,7 +101,7 @@ def test_stage_full_env(mutable_mock_env_path, monkeypatch):
"""Verify that stage filters specs in environment.""" """Verify that stage filters specs in environment."""
e = ev.create("test") e = ev.create("test")
e.add("mpileaks@100.100") e.add("mpileaks@=100.100")
e.concretize() e.concretize()
# list all the package names that should be staged # list all the package names that should be staged

View file

@ -58,8 +58,7 @@ def test_multiple_conflicting_compiler_definitions(mutable_config):
mutable_config.update_config("compilers", compiler_config) mutable_config.update_config("compilers", compiler_config)
arch_spec = spack.spec.ArchSpec(("test", "test", "test")) arch_spec = spack.spec.ArchSpec(("test", "test", "test"))
cspec = compiler_config[0]["compiler"]["spec"] cmp = compilers.compiler_for_spec("clang@=0.0.0", arch_spec)
cmp = compilers.compiler_for_spec(cspec, arch_spec)
assert cmp.f77 == "f77" assert cmp.f77 == "f77"
@ -78,7 +77,7 @@ def test_get_compiler_duplicates(config):
def test_all_compilers(config): def test_all_compilers(config):
all_compilers = compilers.all_compilers() all_compilers = compilers.all_compilers()
filtered = [x for x in all_compilers if str(x.spec) == "clang@3.3"] filtered = [x for x in all_compilers if str(x.spec) == "clang@=3.3"]
filtered = [x for x in filtered if x.operating_system == "SuSE11"] filtered = [x for x in filtered if x.operating_system == "SuSE11"]
assert len(filtered) == 1 assert len(filtered) == 1
@ -525,135 +524,135 @@ def test_gcc_flags():
def test_intel_flags(): def test_intel_flags():
supported_flag_test("openmp_flag", "-openmp", "intel@15.0") supported_flag_test("openmp_flag", "-openmp", "intel@=15.0")
supported_flag_test("openmp_flag", "-qopenmp", "intel@16.0") supported_flag_test("openmp_flag", "-qopenmp", "intel@=16.0")
unsupported_flag_test("cxx11_flag", "intel@11.0") unsupported_flag_test("cxx11_flag", "intel@=11.0")
supported_flag_test("cxx11_flag", "-std=c++0x", "intel@12.0") supported_flag_test("cxx11_flag", "-std=c++0x", "intel@=12.0")
supported_flag_test("cxx11_flag", "-std=c++11", "intel@13") supported_flag_test("cxx11_flag", "-std=c++11", "intel@=13")
unsupported_flag_test("cxx14_flag", "intel@14.0") unsupported_flag_test("cxx14_flag", "intel@=14.0")
supported_flag_test("cxx14_flag", "-std=c++1y", "intel@15.0") supported_flag_test("cxx14_flag", "-std=c++1y", "intel@=15.0")
supported_flag_test("cxx14_flag", "-std=c++14", "intel@15.0.2") supported_flag_test("cxx14_flag", "-std=c++14", "intel@=15.0.2")
unsupported_flag_test("c99_flag", "intel@11.0") unsupported_flag_test("c99_flag", "intel@=11.0")
supported_flag_test("c99_flag", "-std=c99", "intel@12.0") supported_flag_test("c99_flag", "-std=c99", "intel@=12.0")
unsupported_flag_test("c11_flag", "intel@15.0") unsupported_flag_test("c11_flag", "intel@=15.0")
supported_flag_test("c11_flag", "-std=c1x", "intel@16.0") supported_flag_test("c11_flag", "-std=c1x", "intel@=16.0")
supported_flag_test("cc_pic_flag", "-fPIC", "intel@1.0") supported_flag_test("cc_pic_flag", "-fPIC", "intel@=1.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "intel@1.0") supported_flag_test("cxx_pic_flag", "-fPIC", "intel@=1.0")
supported_flag_test("f77_pic_flag", "-fPIC", "intel@1.0") supported_flag_test("f77_pic_flag", "-fPIC", "intel@=1.0")
supported_flag_test("fc_pic_flag", "-fPIC", "intel@1.0") supported_flag_test("fc_pic_flag", "-fPIC", "intel@=1.0")
supported_flag_test("stdcxx_libs", ("-cxxlib",), "intel@1.0") supported_flag_test("stdcxx_libs", ("-cxxlib",), "intel@=1.0")
supported_flag_test("debug_flags", ["-debug", "-g", "-g0", "-g1", "-g2", "-g3"], "intel@1.0") supported_flag_test("debug_flags", ["-debug", "-g", "-g0", "-g1", "-g2", "-g3"], "intel@=1.0")
supported_flag_test( supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os"], "intel@1.0" "opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os"], "intel@=1.0"
) )
def test_oneapi_flags(): def test_oneapi_flags():
supported_flag_test("openmp_flag", "-fiopenmp", "oneapi@2020.8.0.0827") supported_flag_test("openmp_flag", "-fiopenmp", "oneapi@=2020.8.0.0827")
supported_flag_test("cxx11_flag", "-std=c++11", "oneapi@2020.8.0.0827") supported_flag_test("cxx11_flag", "-std=c++11", "oneapi@=2020.8.0.0827")
supported_flag_test("cxx14_flag", "-std=c++14", "oneapi@2020.8.0.0827") supported_flag_test("cxx14_flag", "-std=c++14", "oneapi@=2020.8.0.0827")
supported_flag_test("c99_flag", "-std=c99", "oneapi@2020.8.0.0827") supported_flag_test("c99_flag", "-std=c99", "oneapi@=2020.8.0.0827")
supported_flag_test("c11_flag", "-std=c1x", "oneapi@2020.8.0.0827") supported_flag_test("c11_flag", "-std=c1x", "oneapi@=2020.8.0.0827")
supported_flag_test("cc_pic_flag", "-fPIC", "oneapi@2020.8.0.0827") supported_flag_test("cc_pic_flag", "-fPIC", "oneapi@=2020.8.0.0827")
supported_flag_test("cxx_pic_flag", "-fPIC", "oneapi@2020.8.0.0827") supported_flag_test("cxx_pic_flag", "-fPIC", "oneapi@=2020.8.0.0827")
supported_flag_test("f77_pic_flag", "-fPIC", "oneapi@2020.8.0.0827") supported_flag_test("f77_pic_flag", "-fPIC", "oneapi@=2020.8.0.0827")
supported_flag_test("fc_pic_flag", "-fPIC", "oneapi@2020.8.0.0827") supported_flag_test("fc_pic_flag", "-fPIC", "oneapi@=2020.8.0.0827")
supported_flag_test("stdcxx_libs", ("-cxxlib",), "oneapi@2020.8.0.0827") supported_flag_test("stdcxx_libs", ("-cxxlib",), "oneapi@=2020.8.0.0827")
supported_flag_test( supported_flag_test(
"debug_flags", ["-debug", "-g", "-g0", "-g1", "-g2", "-g3"], "oneapi@2020.8.0.0827" "debug_flags", ["-debug", "-g", "-g0", "-g1", "-g2", "-g3"], "oneapi@=2020.8.0.0827"
) )
supported_flag_test( supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os"], "oneapi@2020.8.0.0827" "opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os"], "oneapi@=2020.8.0.0827"
) )
def test_nag_flags(): def test_nag_flags():
supported_flag_test("openmp_flag", "-openmp", "nag@1.0") supported_flag_test("openmp_flag", "-openmp", "nag@=1.0")
supported_flag_test("cxx11_flag", "-std=c++11", "nag@1.0") supported_flag_test("cxx11_flag", "-std=c++11", "nag@=1.0")
supported_flag_test("cc_pic_flag", "-fPIC", "nag@1.0") supported_flag_test("cc_pic_flag", "-fPIC", "nag@=1.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "nag@1.0") supported_flag_test("cxx_pic_flag", "-fPIC", "nag@=1.0")
supported_flag_test("f77_pic_flag", "-PIC", "nag@1.0") supported_flag_test("f77_pic_flag", "-PIC", "nag@=1.0")
supported_flag_test("fc_pic_flag", "-PIC", "nag@1.0") supported_flag_test("fc_pic_flag", "-PIC", "nag@=1.0")
supported_flag_test("cc_rpath_arg", "-Wl,-rpath,", "nag@1.0") supported_flag_test("cc_rpath_arg", "-Wl,-rpath,", "nag@=1.0")
supported_flag_test("cxx_rpath_arg", "-Wl,-rpath,", "nag@1.0") supported_flag_test("cxx_rpath_arg", "-Wl,-rpath,", "nag@=1.0")
supported_flag_test("f77_rpath_arg", "-Wl,-Wl,,-rpath,,", "nag@1.0") supported_flag_test("f77_rpath_arg", "-Wl,-Wl,,-rpath,,", "nag@=1.0")
supported_flag_test("fc_rpath_arg", "-Wl,-Wl,,-rpath,,", "nag@1.0") supported_flag_test("fc_rpath_arg", "-Wl,-Wl,,-rpath,,", "nag@=1.0")
supported_flag_test("linker_arg", "-Wl,-Wl,,", "nag@1.0") supported_flag_test("linker_arg", "-Wl,-Wl,,", "nag@=1.0")
supported_flag_test("debug_flags", ["-g", "-gline", "-g90"], "nag@1.0") supported_flag_test("debug_flags", ["-g", "-gline", "-g90"], "nag@=1.0")
supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"], "nag@1.0") supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"], "nag@=1.0")
def test_nvhpc_flags(): def test_nvhpc_flags():
supported_flag_test("openmp_flag", "-mp", "nvhpc@20.9") supported_flag_test("openmp_flag", "-mp", "nvhpc@=20.9")
supported_flag_test("cxx11_flag", "--c++11", "nvhpc@20.9") supported_flag_test("cxx11_flag", "--c++11", "nvhpc@=20.9")
supported_flag_test("cxx14_flag", "--c++14", "nvhpc@20.9") supported_flag_test("cxx14_flag", "--c++14", "nvhpc@=20.9")
supported_flag_test("cxx17_flag", "--c++17", "nvhpc@20.9") supported_flag_test("cxx17_flag", "--c++17", "nvhpc@=20.9")
supported_flag_test("c99_flag", "-c99", "nvhpc@20.9") supported_flag_test("c99_flag", "-c99", "nvhpc@=20.9")
supported_flag_test("c11_flag", "-c11", "nvhpc@20.9") supported_flag_test("c11_flag", "-c11", "nvhpc@=20.9")
supported_flag_test("cc_pic_flag", "-fpic", "nvhpc@20.9") supported_flag_test("cc_pic_flag", "-fpic", "nvhpc@=20.9")
supported_flag_test("cxx_pic_flag", "-fpic", "nvhpc@20.9") supported_flag_test("cxx_pic_flag", "-fpic", "nvhpc@=20.9")
supported_flag_test("f77_pic_flag", "-fpic", "nvhpc@20.9") supported_flag_test("f77_pic_flag", "-fpic", "nvhpc@=20.9")
supported_flag_test("fc_pic_flag", "-fpic", "nvhpc@20.9") supported_flag_test("fc_pic_flag", "-fpic", "nvhpc@=20.9")
supported_flag_test("debug_flags", ["-g", "-gopt"], "nvhpc@20.9") supported_flag_test("debug_flags", ["-g", "-gopt"], "nvhpc@=20.9")
supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"], "nvhpc@20.9") supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"], "nvhpc@=20.9")
supported_flag_test("stdcxx_libs", ("-c++libs",), "nvhpc@20.9") supported_flag_test("stdcxx_libs", ("-c++libs",), "nvhpc@=20.9")
def test_pgi_flags(): def test_pgi_flags():
supported_flag_test("openmp_flag", "-mp", "pgi@1.0") supported_flag_test("openmp_flag", "-mp", "pgi@=1.0")
supported_flag_test("cxx11_flag", "-std=c++11", "pgi@1.0") supported_flag_test("cxx11_flag", "-std=c++11", "pgi@=1.0")
unsupported_flag_test("c99_flag", "pgi@12.9") unsupported_flag_test("c99_flag", "pgi@=12.9")
supported_flag_test("c99_flag", "-c99", "pgi@12.10") supported_flag_test("c99_flag", "-c99", "pgi@=12.10")
unsupported_flag_test("c11_flag", "pgi@15.2") unsupported_flag_test("c11_flag", "pgi@=15.2")
supported_flag_test("c11_flag", "-c11", "pgi@15.3") supported_flag_test("c11_flag", "-c11", "pgi@=15.3")
supported_flag_test("cc_pic_flag", "-fpic", "pgi@1.0") supported_flag_test("cc_pic_flag", "-fpic", "pgi@=1.0")
supported_flag_test("cxx_pic_flag", "-fpic", "pgi@1.0") supported_flag_test("cxx_pic_flag", "-fpic", "pgi@=1.0")
supported_flag_test("f77_pic_flag", "-fpic", "pgi@1.0") supported_flag_test("f77_pic_flag", "-fpic", "pgi@=1.0")
supported_flag_test("fc_pic_flag", "-fpic", "pgi@1.0") supported_flag_test("fc_pic_flag", "-fpic", "pgi@=1.0")
supported_flag_test("stdcxx_libs", ("-pgc++libs",), "pgi@1.0") supported_flag_test("stdcxx_libs", ("-pgc++libs",), "pgi@=1.0")
supported_flag_test("debug_flags", ["-g", "-gopt"], "pgi@1.0") supported_flag_test("debug_flags", ["-g", "-gopt"], "pgi@=1.0")
supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"], "pgi@1.0") supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"], "pgi@=1.0")
def test_xl_flags(): def test_xl_flags():
supported_flag_test("openmp_flag", "-qsmp=omp", "xl@1.0") supported_flag_test("openmp_flag", "-qsmp=omp", "xl@=1.0")
unsupported_flag_test("cxx11_flag", "xl@13.0") unsupported_flag_test("cxx11_flag", "xl@=13.0")
supported_flag_test("cxx11_flag", "-qlanglvl=extended0x", "xl@13.1") supported_flag_test("cxx11_flag", "-qlanglvl=extended0x", "xl@=13.1")
unsupported_flag_test("c99_flag", "xl@10.0") unsupported_flag_test("c99_flag", "xl@=10.0")
supported_flag_test("c99_flag", "-qlanglvl=extc99", "xl@10.1") supported_flag_test("c99_flag", "-qlanglvl=extc99", "xl@=10.1")
supported_flag_test("c99_flag", "-std=gnu99", "xl@13.1.1") supported_flag_test("c99_flag", "-std=gnu99", "xl@=13.1.1")
unsupported_flag_test("c11_flag", "xl@12.0") unsupported_flag_test("c11_flag", "xl@=12.0")
supported_flag_test("c11_flag", "-qlanglvl=extc1x", "xl@12.1") supported_flag_test("c11_flag", "-qlanglvl=extc1x", "xl@=12.1")
supported_flag_test("c11_flag", "-std=gnu11", "xl@13.1.2") supported_flag_test("c11_flag", "-std=gnu11", "xl@=13.1.2")
supported_flag_test("cc_pic_flag", "-qpic", "xl@1.0") supported_flag_test("cc_pic_flag", "-qpic", "xl@=1.0")
supported_flag_test("cxx_pic_flag", "-qpic", "xl@1.0") supported_flag_test("cxx_pic_flag", "-qpic", "xl@=1.0")
supported_flag_test("f77_pic_flag", "-qpic", "xl@1.0") supported_flag_test("f77_pic_flag", "-qpic", "xl@=1.0")
supported_flag_test("fc_pic_flag", "-qpic", "xl@1.0") supported_flag_test("fc_pic_flag", "-qpic", "xl@=1.0")
supported_flag_test("fflags", "-qzerosize", "xl@1.0") supported_flag_test("fflags", "-qzerosize", "xl@=1.0")
supported_flag_test("debug_flags", ["-g", "-g0", "-g1", "-g2", "-g8", "-g9"], "xl@1.0") supported_flag_test("debug_flags", ["-g", "-g0", "-g1", "-g2", "-g8", "-g9"], "xl@=1.0")
supported_flag_test( supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4", "-O5", "-Ofast"], "xl@1.0" "opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4", "-O5", "-Ofast"], "xl@=1.0"
) )
def test_xl_r_flags(): def test_xl_r_flags():
supported_flag_test("openmp_flag", "-qsmp=omp", "xl_r@1.0") supported_flag_test("openmp_flag", "-qsmp=omp", "xl_r@=1.0")
unsupported_flag_test("cxx11_flag", "xl_r@13.0") unsupported_flag_test("cxx11_flag", "xl_r@=13.0")
supported_flag_test("cxx11_flag", "-qlanglvl=extended0x", "xl_r@13.1") supported_flag_test("cxx11_flag", "-qlanglvl=extended0x", "xl_r@=13.1")
unsupported_flag_test("c99_flag", "xl_r@10.0") unsupported_flag_test("c99_flag", "xl_r@=10.0")
supported_flag_test("c99_flag", "-qlanglvl=extc99", "xl_r@10.1") supported_flag_test("c99_flag", "-qlanglvl=extc99", "xl_r@=10.1")
supported_flag_test("c99_flag", "-std=gnu99", "xl_r@13.1.1") supported_flag_test("c99_flag", "-std=gnu99", "xl_r@=13.1.1")
unsupported_flag_test("c11_flag", "xl_r@12.0") unsupported_flag_test("c11_flag", "xl_r@=12.0")
supported_flag_test("c11_flag", "-qlanglvl=extc1x", "xl_r@12.1") supported_flag_test("c11_flag", "-qlanglvl=extc1x", "xl_r@=12.1")
supported_flag_test("c11_flag", "-std=gnu11", "xl_r@13.1.2") supported_flag_test("c11_flag", "-std=gnu11", "xl_r@=13.1.2")
supported_flag_test("cc_pic_flag", "-qpic", "xl_r@1.0") supported_flag_test("cc_pic_flag", "-qpic", "xl_r@=1.0")
supported_flag_test("cxx_pic_flag", "-qpic", "xl_r@1.0") supported_flag_test("cxx_pic_flag", "-qpic", "xl_r@=1.0")
supported_flag_test("f77_pic_flag", "-qpic", "xl_r@1.0") supported_flag_test("f77_pic_flag", "-qpic", "xl_r@=1.0")
supported_flag_test("fc_pic_flag", "-qpic", "xl_r@1.0") supported_flag_test("fc_pic_flag", "-qpic", "xl_r@=1.0")
supported_flag_test("fflags", "-qzerosize", "xl_r@1.0") supported_flag_test("fflags", "-qzerosize", "xl_r@=1.0")
supported_flag_test("debug_flags", ["-g", "-g0", "-g1", "-g2", "-g8", "-g9"], "xl@1.0") supported_flag_test("debug_flags", ["-g", "-g0", "-g1", "-g2", "-g8", "-g9"], "xl@=1.0")
supported_flag_test( supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4", "-O5", "-Ofast"], "xl@1.0" "opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4", "-O5", "-Ofast"], "xl@=1.0"
) )
@ -662,8 +661,8 @@ def test_xl_r_flags():
[("gcc@4.7.2", False), ("clang@3.3", False), ("clang@8.0.0", True)], [("gcc@4.7.2", False), ("clang@3.3", False), ("clang@8.0.0", True)],
) )
def test_detecting_mixed_toolchains(compiler_spec, expected_result, config): def test_detecting_mixed_toolchains(compiler_spec, expected_result, config):
compiler = spack.compilers.compilers_for_spec(compiler_spec).pop() compiler = compilers.compilers_for_spec(compiler_spec).pop()
assert spack.compilers.is_mixed_toolchain(compiler) is expected_result assert compilers.is_mixed_toolchain(compiler) is expected_result
@pytest.mark.regression("14798,13733") @pytest.mark.regression("14798,13733")
@ -692,7 +691,7 @@ def test_raising_if_compiler_target_is_over_specific(config):
with spack.config.override("compilers", compilers): with spack.config.override("compilers", compilers):
cfg = spack.compilers.get_compiler_config() cfg = spack.compilers.get_compiler_config()
with pytest.raises(ValueError): with pytest.raises(ValueError):
spack.compilers.get_compilers(cfg, "gcc@9.0.1", arch_spec) spack.compilers.get_compilers(cfg, spack.spec.CompilerSpec("gcc@9.0.1"), arch_spec)
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
@ -844,7 +843,7 @@ class MockPackage(object):
apple_clang_cls = spack.compilers.class_for_compiler_name("apple-clang") apple_clang_cls = spack.compilers.class_for_compiler_name("apple-clang")
compiler = apple_clang_cls( compiler = apple_clang_cls(
spack.spec.CompilerSpec("apple-clang@11.0.0"), spack.spec.CompilerSpec("apple-clang@=11.0.0"),
"catalina", "catalina",
"x86_64", "x86_64",
["/usr/bin/clang", "/usr/bin/clang++", None, None], ["/usr/bin/clang", "/usr/bin/clang++", None, None],

View file

@ -15,6 +15,7 @@
import spack.compilers import spack.compilers
import spack.concretize import spack.concretize
import spack.config
import spack.detection import spack.detection
import spack.error import spack.error
import spack.hash_types as ht import spack.hash_types as ht
@ -22,7 +23,7 @@
import spack.repo import spack.repo
import spack.variant as vt import spack.variant as vt
from spack.concretize import find_spec from spack.concretize import find_spec
from spack.spec import Spec from spack.spec import CompilerSpec, Spec
from spack.version import ver from spack.version import ver
@ -148,10 +149,10 @@ class Root(Package):
homepage = "http://www.example.com" homepage = "http://www.example.com"
url = "http://www.example.com/root-1.0.tar.gz" url = "http://www.example.com/root-1.0.tar.gz"
version(1.0, sha256='abcde') version("1.0", sha256="abcde")
depends_on('changing') depends_on("changing")
conflicts('changing~foo') conflicts("changing~foo")
""" """
packages_dir.join("root", "package.py").write(root_pkg_str, ensure=True) packages_dir.join("root", "package.py").write(root_pkg_str, ensure=True)
@ -162,17 +163,17 @@ class Changing(Package):
{% if not delete_version %} {% if not delete_version %}
version(1.0, sha256='abcde') version("1.0", sha256="abcde")
{% endif %} {% endif %}
version(0.9, sha256='abcde') version("0.9", sha256="abcde")
{% if not delete_variant %} {% if not delete_variant %}
variant('fee', default=True, description='nope') variant("fee", default=True, description="nope")
{% endif %} {% endif %}
variant('foo', default=True, description='nope') variant("foo", default=True, description="nope")
{% if add_variant %} {% if add_variant %}
variant('fum', default=True, description='nope') variant("fum", default=True, description="nope")
variant('fum2', default=True, description='nope') variant("fum2", default=True, description="nope")
{% endif %} {% endif %}
""" """
@ -228,7 +229,7 @@ def test_concretize(self, spec):
check_concretize(spec) check_concretize(spec)
def test_concretize_mention_build_dep(self): def test_concretize_mention_build_dep(self):
spec = check_concretize("cmake-client ^cmake@3.21.3") spec = check_concretize("cmake-client ^cmake@=3.21.3")
# Check parent's perspective of child # Check parent's perspective of child
to_dependencies = spec.edges_to_dependencies(name="cmake") to_dependencies = spec.edges_to_dependencies(name="cmake")
@ -243,9 +244,9 @@ def test_concretize_mention_build_dep(self):
def test_concretize_preferred_version(self): def test_concretize_preferred_version(self):
spec = check_concretize("python") spec = check_concretize("python")
assert spec.versions == ver("2.7.11") assert spec.version == ver("=2.7.11")
spec = check_concretize("python@3.5.1") spec = check_concretize("python@3.5.1")
assert spec.versions == ver("3.5.1") assert spec.version == ver("=3.5.1")
def test_concretize_with_restricted_virtual(self): def test_concretize_with_restricted_virtual(self):
check_concretize("mpileaks ^mpich2") check_concretize("mpileaks ^mpich2")
@ -280,10 +281,10 @@ def test_concretize_with_restricted_virtual(self):
def test_concretize_enable_disable_compiler_existence_check(self): def test_concretize_enable_disable_compiler_existence_check(self):
with spack.concretize.enable_compiler_existence_check(): with spack.concretize.enable_compiler_existence_check():
with pytest.raises(spack.concretize.UnavailableCompilerVersionError): with pytest.raises(spack.concretize.UnavailableCompilerVersionError):
check_concretize("dttop %gcc@100.100") check_concretize("dttop %gcc@=100.100")
with spack.concretize.disable_compiler_existence_check(): with spack.concretize.disable_compiler_existence_check():
spec = check_concretize("dttop %gcc@100.100") spec = check_concretize("dttop %gcc@=100.100")
assert spec.satisfies("%gcc@100.100") assert spec.satisfies("%gcc@100.100")
assert spec["dtlink3"].satisfies("%gcc@100.100") assert spec["dtlink3"].satisfies("%gcc@100.100")
@ -335,7 +336,7 @@ def test_compiler_flags_differ_identical_compilers(self):
spec = Spec("a %clang@12.2.0 platform=test os=fe target=fe") spec = Spec("a %clang@12.2.0 platform=test os=fe target=fe")
# Get the compiler that matches the spec ( # Get the compiler that matches the spec (
compiler = spack.compilers.compiler_for_spec("clang@12.2.0", spec.architecture) compiler = spack.compilers.compiler_for_spec("clang@=12.2.0", spec.architecture)
# Clear cache for compiler config since it has its own cache mechanism outside of config # Clear cache for compiler config since it has its own cache mechanism outside of config
spack.compilers._cache_config_file = [] spack.compilers._cache_config_file = []
@ -479,7 +480,7 @@ def test_concretize_propagated_variant_is_not_passed_to_dependent(self):
def test_no_matching_compiler_specs(self, mock_low_high_config): def test_no_matching_compiler_specs(self, mock_low_high_config):
# only relevant when not building compilers as needed # only relevant when not building compilers as needed
with spack.concretize.enable_compiler_existence_check(): with spack.concretize.enable_compiler_existence_check():
s = Spec("a %gcc@0.0.0") s = Spec("a %gcc@=0.0.0")
with pytest.raises(spack.concretize.UnavailableCompilerVersionError): with pytest.raises(spack.concretize.UnavailableCompilerVersionError):
s.concretize() s.concretize()
@ -748,10 +749,10 @@ def test_noversion_pkg(self, spec):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"spec, best_achievable", "spec, best_achievable",
[ [
("mpileaks%gcc@4.4.7 ^dyninst@10.2.1 target=x86_64:", "core2"), ("mpileaks%gcc@=4.4.7 ^dyninst@=10.2.1 target=x86_64:", "core2"),
("mpileaks%gcc@4.8 target=x86_64:", "haswell"), ("mpileaks%gcc@=4.8 target=x86_64:", "haswell"),
("mpileaks%gcc@5.3.0 target=x86_64:", "broadwell"), ("mpileaks%gcc@=5.3.0 target=x86_64:", "broadwell"),
("mpileaks%apple-clang@5.1.0 target=x86_64:", "x86_64"), ("mpileaks%apple-clang@=5.1.0 target=x86_64:", "x86_64"),
], ],
) )
@pytest.mark.regression("13361", "20537") @pytest.mark.regression("13361", "20537")
@ -764,18 +765,15 @@ def test_adjusting_default_target_based_on_compiler(
s = Spec(spec).concretized() s = Spec(spec).concretized()
assert str(s.architecture.target) == str(expected) assert str(s.architecture.target) == str(expected)
@pytest.mark.regression("8735,14730")
def test_compiler_version_matches_any_entry_in_compilers_yaml(self): def test_compiler_version_matches_any_entry_in_compilers_yaml(self):
# Ensure that a concrete compiler with different compiler version # The behavior here has changed since #8735 / #14730. Now %gcc@10.2 is an abstract
# doesn't match (here it's 10.2 vs. 10.2.1) # compiler spec, and it should first find a matching compiler gcc@=10.2.1
with pytest.raises(spack.concretize.UnavailableCompilerVersionError): assert Spec("mpileaks %gcc@10.2").concretized().compiler == CompilerSpec("gcc@=10.2.1")
s = Spec("mpileaks %gcc@10.2") assert Spec("mpileaks %gcc@10.2:").concretized().compiler == CompilerSpec("gcc@=10.2.1")
s.concretize()
# An abstract compiler with a version list could resolve to 4.5.0 # This compiler does not exist
s = Spec("mpileaks %gcc@10.2:") with pytest.raises(spack.concretize.UnavailableCompilerVersionError):
s.concretize() Spec("mpileaks %gcc@=10.2").concretized()
assert str(s.compiler.version) == "10.2.1"
def test_concretize_anonymous(self): def test_concretize_anonymous(self):
with pytest.raises(spack.error.SpackError): with pytest.raises(spack.error.SpackError):
@ -1158,7 +1156,7 @@ def test_all_patches_applied(self):
else "d0df7988457ec999c148a4a2af25ce831bfaad13954ba18a4446374cb0aef55e" else "d0df7988457ec999c148a4a2af25ce831bfaad13954ba18a4446374cb0aef55e"
) )
localpatch = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" localpatch = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
spec = spack.spec.Spec("conditionally-patch-dependency+jasper") spec = Spec("conditionally-patch-dependency+jasper")
spec.concretize() spec.concretize()
assert (uuidpatch, localpatch) == spec["libelf"].variants["patches"].value assert (uuidpatch, localpatch) == spec["libelf"].variants["patches"].value
@ -1411,14 +1409,14 @@ def test_version_badness_more_important_than_default_mv_variants(self):
# a transitive dependency with a multi-valued variant, that old # a transitive dependency with a multi-valued variant, that old
# version was preferred because of the order of our optimization # version was preferred because of the order of our optimization
# criteria. # criteria.
s = spack.spec.Spec("root").concretized() s = Spec("root").concretized()
assert s["gmt"].satisfies("@2.0") assert s["gmt"].satisfies("@2.0")
@pytest.mark.regression("24205") @pytest.mark.regression("24205")
def test_provider_must_meet_requirements(self): def test_provider_must_meet_requirements(self):
# A package can be a provider of a virtual only if the underlying # A package can be a provider of a virtual only if the underlying
# requirements are met. # requirements are met.
s = spack.spec.Spec("unsat-virtual-dependency") s = Spec("unsat-virtual-dependency")
with pytest.raises((RuntimeError, spack.error.UnsatisfiableSpecError)): with pytest.raises((RuntimeError, spack.error.UnsatisfiableSpecError)):
s.concretize() s.concretize()
@ -1432,7 +1430,7 @@ def test_newer_dependency_adds_a_transitive_virtual(self):
# root@1.0 <- middle@1.0 <- leaf@1.0 # root@1.0 <- middle@1.0 <- leaf@1.0
# #
# and "blas" is pulled in only by newer versions of "leaf" # and "blas" is pulled in only by newer versions of "leaf"
s = spack.spec.Spec("root-adds-virtual").concretized() s = Spec("root-adds-virtual").concretized()
assert s["leaf-adds-virtual"].satisfies("@2.0") assert s["leaf-adds-virtual"].satisfies("@2.0")
assert "blas" in s assert "blas" in s
@ -1440,12 +1438,12 @@ def test_newer_dependency_adds_a_transitive_virtual(self):
def test_versions_in_virtual_dependencies(self): def test_versions_in_virtual_dependencies(self):
# Ensure that a package that needs a given version of a virtual # Ensure that a package that needs a given version of a virtual
# package doesn't end up using a later implementation # package doesn't end up using a later implementation
s = spack.spec.Spec("hpcviewer@2019.02").concretized() s = Spec("hpcviewer@2019.02").concretized()
assert s["java"].satisfies("virtual-with-versions@1.8.0") assert s["java"].satisfies("virtual-with-versions@1.8.0")
@pytest.mark.regression("26866") @pytest.mark.regression("26866")
def test_non_default_provider_of_multiple_virtuals(self): def test_non_default_provider_of_multiple_virtuals(self):
s = spack.spec.Spec("many-virtual-consumer ^low-priority-provider").concretized() s = Spec("many-virtual-consumer ^low-priority-provider").concretized()
assert s["mpi"].name == "low-priority-provider" assert s["mpi"].name == "low-priority-provider"
assert s["lapack"].name == "low-priority-provider" assert s["lapack"].name == "low-priority-provider"
@ -1471,7 +1469,7 @@ def test_concrete_specs_are_not_modified_on_reuse(
# like additional constraints being added to concrete specs in # like additional constraints being added to concrete specs in
# the answer set produced by clingo. # the answer set produced by clingo.
with spack.config.override("concretizer:reuse", True): with spack.config.override("concretizer:reuse", True):
s = spack.spec.Spec(spec_str).concretized() s = Spec(spec_str).concretized()
assert s.installed is expect_installed assert s.installed is expect_installed
assert s.satisfies(spec_str) assert s.satisfies(spec_str)
@ -1485,12 +1483,12 @@ def test_sticky_variant_in_package(self):
# to have +allow-gcc set to be concretized with %gcc and clingo is not allowed # to have +allow-gcc set to be concretized with %gcc and clingo is not allowed
# to change the default ~allow-gcc # to change the default ~allow-gcc
with pytest.raises(spack.error.SpackError): with pytest.raises(spack.error.SpackError):
spack.spec.Spec("sticky-variant %gcc").concretized() Spec("sticky-variant %gcc").concretized()
s = spack.spec.Spec("sticky-variant+allow-gcc %gcc").concretized() s = Spec("sticky-variant+allow-gcc %gcc").concretized()
assert s.satisfies("%gcc") and s.satisfies("+allow-gcc") assert s.satisfies("%gcc") and s.satisfies("+allow-gcc")
s = spack.spec.Spec("sticky-variant %clang").concretized() s = Spec("sticky-variant %clang").concretized()
assert s.satisfies("%clang") and s.satisfies("~allow-gcc") assert s.satisfies("%clang") and s.satisfies("~allow-gcc")
def test_do_not_invent_new_concrete_versions_unless_necessary(self): def test_do_not_invent_new_concrete_versions_unless_necessary(self):
@ -1499,10 +1497,10 @@ def test_do_not_invent_new_concrete_versions_unless_necessary(self):
# ensure we select a known satisfying version rather than creating # ensure we select a known satisfying version rather than creating
# a new '2.7' version. # a new '2.7' version.
assert ver("2.7.11") == Spec("python@2.7").concretized().version assert ver("=2.7.11") == Spec("python@2.7").concretized().version
# Here there is no known satisfying version - use the one on the spec. # Here there is no known satisfying version - use the one on the spec.
assert ver("2.7.21") == Spec("python@2.7.21").concretized().version assert ver("=2.7.21") == Spec("python@=2.7.21").concretized().version
@pytest.mark.parametrize( @pytest.mark.parametrize(
"spec_str,valid", "spec_str,valid",
@ -1663,7 +1661,7 @@ def test_best_effort_coconcretize(self, specs, expected):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer cannot concretize in rounds") pytest.skip("Original concretizer cannot concretize in rounds")
specs = [spack.spec.Spec(s) for s in specs] specs = [Spec(s) for s in specs]
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
solver.reuse = False solver.reuse = False
concrete_specs = set() concrete_specs = set()
@ -1710,7 +1708,7 @@ def test_best_effort_coconcretize_preferences(self, specs, expected_spec, occura
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer cannot concretize in rounds") pytest.skip("Original concretizer cannot concretize in rounds")
specs = [spack.spec.Spec(s) for s in specs] specs = [Spec(s) for s in specs]
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
solver.reuse = False solver.reuse = False
concrete_specs = {} concrete_specs = {}
@ -1731,9 +1729,9 @@ def test_coconcretize_reuse_and_virtuals(self):
reusable_specs = [] reusable_specs = []
for s in ["mpileaks ^mpich", "zmpi"]: for s in ["mpileaks ^mpich", "zmpi"]:
reusable_specs.extend(spack.spec.Spec(s).concretized().traverse(root=True)) reusable_specs.extend(Spec(s).concretized().traverse(root=True))
root_specs = [spack.spec.Spec("mpileaks"), spack.spec.Spec("zmpi")] root_specs = [Spec("mpileaks"), Spec("zmpi")]
import spack.solver.asp import spack.solver.asp
@ -1755,8 +1753,8 @@ def test_misleading_error_message_on_version(self, mutable_database):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer cannot reuse") pytest.skip("Original concretizer cannot reuse")
reusable_specs = [spack.spec.Spec("non-existing-conditional-dep@1.0").concretized()] reusable_specs = [Spec("non-existing-conditional-dep@1.0").concretized()]
root_spec = spack.spec.Spec("non-existing-conditional-dep@2.0") root_spec = Spec("non-existing-conditional-dep@2.0")
with spack.config.override("concretizer:reuse", True): with spack.config.override("concretizer:reuse", True):
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
@ -1774,10 +1772,8 @@ def test_version_weight_and_provenance(self):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer cannot reuse") pytest.skip("Original concretizer cannot reuse")
reusable_specs = [ reusable_specs = [Spec(spec_str).concretized() for spec_str in ("b@0.9", "b@1.0")]
spack.spec.Spec(spec_str).concretized() for spec_str in ("b@0.9", "b@1.0") root_spec = Spec("a foobar=bar")
]
root_spec = spack.spec.Spec("a foobar=bar")
with spack.config.override("concretizer:reuse", True): with spack.config.override("concretizer:reuse", True):
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
@ -1811,7 +1807,7 @@ def test_not_reusing_incompatible_os_or_compiler(self):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer cannot reuse") pytest.skip("Original concretizer cannot reuse")
root_spec = spack.spec.Spec("b") root_spec = Spec("b")
s = root_spec.concretized() s = root_spec.concretized()
wrong_compiler, wrong_os = s.copy(), s.copy() wrong_compiler, wrong_os = s.copy(), s.copy()
wrong_compiler.compiler = spack.spec.CompilerSpec("gcc@12.1.0") wrong_compiler.compiler = spack.spec.CompilerSpec("gcc@12.1.0")
@ -1926,7 +1922,7 @@ def test_installed_specs_disregard_conflicts(self, mutable_database, monkeypatch
# Add a conflict to "mpich" that match an already installed "mpich~debug" # Add a conflict to "mpich" that match an already installed "mpich~debug"
pkg_cls = spack.repo.path.get_pkg_class("mpich") pkg_cls = spack.repo.path.get_pkg_class("mpich")
monkeypatch.setitem(pkg_cls.conflicts, "~debug", [(spack.spec.Spec(), None)]) monkeypatch.setitem(pkg_cls.conflicts, "~debug", [(Spec(), None)])
# If we concretize with --fresh the conflict is taken into account # If we concretize with --fresh the conflict is taken into account
with spack.config.override("concretizer:reuse", False): with spack.config.override("concretizer:reuse", False):
@ -1998,7 +1994,7 @@ def test_external_python_extension_find_dependency_from_config(self, python_spec
assert "python" in spec["py-extension1"] assert "python" in spec["py-extension1"]
assert spec["python"].prefix == fake_path assert spec["python"].prefix == fake_path
# The spec is not equal to spack.spec.Spec("python@configured") because it gets a # The spec is not equal to Spec("python@configured") because it gets a
# namespace and an external prefix before marking concrete # namespace and an external prefix before marking concrete
assert spec["python"].satisfies(python_spec) assert spec["python"].satisfies(python_spec)
@ -2029,7 +2025,7 @@ def test_external_python_extension_find_dependency_from_installed(self, monkeypa
assert "python" in spec["py-extension1"] assert "python" in spec["py-extension1"]
assert spec["python"].prefix == fake_path assert spec["python"].prefix == fake_path
# The spec is not equal to spack.spec.Spec("python@configured") because it gets a # The spec is not equal to Spec("python@configured") because it gets a
# namespace and an external prefix before marking concrete # namespace and an external prefix before marking concrete
assert spec["python"].satisfies(python) assert spec["python"].satisfies(python)
@ -2037,7 +2033,7 @@ def test_external_python_extension_find_dependency_from_detection(self, monkeypa
"""Test that python extensions have access to a python dependency """Test that python extensions have access to a python dependency
when python isn't otherwise in the DAG""" when python isn't otherwise in the DAG"""
python_spec = spack.spec.Spec("python@detected") python_spec = Spec("python@=detected")
prefix = os.path.sep + "fake" prefix = os.path.sep + "fake"
def find_fake_python(classes, path_hints): def find_fake_python(classes, path_hints):
@ -2068,7 +2064,7 @@ def test_external_python_extension_find_unified_python(self):
} }
spack.config.set("packages", external_conf) spack.config.set("packages", external_conf)
abstract_specs = [spack.spec.Spec(s) for s in ["py-extension1", "python"]] abstract_specs = [Spec(s) for s in ["py-extension1", "python"]]
specs = spack.concretize.concretize_specs_together(*abstract_specs) specs = spack.concretize.concretize_specs_together(*abstract_specs)
assert specs[0]["python"] == specs[1]["python"] assert specs[0]["python"] == specs[1]["python"]
@ -2085,7 +2081,7 @@ def test_result_specs_is_not_empty(self, specs):
"""Check that the implementation of "result.specs" is correct in cases where we """Check that the implementation of "result.specs" is correct in cases where we
know a concretization exists. know a concretization exists.
""" """
specs = [spack.spec.Spec(s) for s in specs] specs = [Spec(s) for s in specs]
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
setup = spack.solver.asp.SpackSolverSetup() setup = spack.solver.asp.SpackSolverSetup()
result, _, _ = solver.driver.solve(setup, specs, reuse=[]) result, _, _ = solver.driver.solve(setup, specs, reuse=[])
@ -2127,8 +2123,8 @@ def test_compiler_match_constraints_when_selected(self):
}, },
] ]
spack.config.set("compilers", compiler_configuration) spack.config.set("compilers", compiler_configuration)
s = spack.spec.Spec("a %gcc@:11").concretized() s = Spec("a %gcc@:11").concretized()
assert s.compiler.version == ver("11.1.0"), s assert s.compiler.version == ver("=11.1.0"), s
@pytest.mark.regression("36339") @pytest.mark.regression("36339")
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows")
@ -2148,8 +2144,8 @@ def test_compiler_with_custom_non_numeric_version(self, mock_executable):
} }
] ]
spack.config.set("compilers", compiler_configuration) spack.config.set("compilers", compiler_configuration)
s = spack.spec.Spec("a %gcc@foo").concretized() s = Spec("a %gcc@foo").concretized()
assert s.compiler.version == ver("foo") assert s.compiler.version == ver("=foo")
@pytest.mark.regression("36628") @pytest.mark.regression("36628")
def test_concretization_with_compilers_supporting_target_any(self): def test_concretization_with_compilers_supporting_target_any(self):

View file

@ -13,7 +13,7 @@
import spack.repo import spack.repo
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
from spack.config import ConfigError from spack.config import ConfigError
from spack.spec import Spec from spack.spec import CompilerSpec, Spec
from spack.version import Version from spack.version import Version
@ -109,10 +109,13 @@ def test_preferred_variants_from_wildcard(self):
) )
def test_preferred_compilers(self, compiler_str, spec_str): def test_preferred_compilers(self, compiler_str, spec_str):
"""Test preferred compilers are applied correctly""" """Test preferred compilers are applied correctly"""
spec = spack.spec.Spec(spec_str) spec = Spec(spec_str)
update_packages(spec.name, "compiler", [compiler_str]) update_packages(spec.name, "compiler", [compiler_str])
spec.concretize() spec.concretize()
assert spec.compiler == spack.spec.CompilerSpec(compiler_str) # note: lhs has concrete compiler version, rhs still abstract.
# Could be made more strict by checking for equality with `gcc@=4.5.0`
# etc.
assert spec.compiler.satisfies(CompilerSpec(compiler_str))
def test_preferred_target(self, mutable_mock_repo): def test_preferred_target(self, mutable_mock_repo):
"""Test preferred targets are applied correctly""" """Test preferred targets are applied correctly"""

View file

@ -9,8 +9,10 @@
import spack.build_systems.generic import spack.build_systems.generic
import spack.config import spack.config
import spack.package_base
import spack.repo import spack.repo
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
import spack.version
from spack.solver.asp import UnsatisfiableSpecError from spack.solver.asp import UnsatisfiableSpecError
from spack.spec import Spec from spack.spec import Spec
from spack.util.url import path_to_file_url from spack.util.url import path_to_file_url
@ -27,14 +29,14 @@ def update_packages_config(conf_str):
"x", "x",
"""\ """\
class X(Package): class X(Package):
version('1.1') version("1.1")
version('1.0') version("1.0")
version('0.9') version("0.9")
variant('shared', default=True, variant("shared", default=True,
description='Build shared libraries') description="Build shared libraries")
depends_on('y') depends_on("y")
""", """,
) )
@ -43,12 +45,12 @@ class X(Package):
"y", "y",
"""\ """\
class Y(Package): class Y(Package):
version('2.5') version("2.5")
version('2.4') version("2.4")
version('2.3', deprecated=True) version("2.3", deprecated=True)
variant('shared', default=True, variant("shared", default=True,
description='Build shared libraries') description="Build shared libraries")
""", """,
) )
@ -57,8 +59,8 @@ class Y(Package):
"v", "v",
"""\ """\
class V(Package): class V(Package):
version('2.1') version("2.1")
version('2.0') version("2.0")
""", """,
) )
@ -150,27 +152,50 @@ def test_git_user_supplied_reference_satisfaction(
spack.package_base.PackageBase, "git", path_to_file_url(repo_path), raising=False spack.package_base.PackageBase, "git", path_to_file_url(repo_path), raising=False
) )
specs = ["v@{commit0}=2.2", "v@{commit0}", "v@2.2", "v@{commit0}=2.3"] hash_eq_ver = Spec(f"v@{commits[0]}=2.2")
hash_eq_ver_copy = Spec(f"v@{commits[0]}=2.2")
just_hash = Spec(f"v@{commits[0]}")
just_ver = Spec("v@=2.2")
hash_eq_other_ver = Spec(f"v@{commits[0]}=2.3")
format_info = {"commit0": commits[0]} assert not hash_eq_ver == just_hash
assert not hash_eq_ver.satisfies(just_hash)
assert not hash_eq_ver.intersects(just_hash)
hash_eq_ver, just_hash, just_ver, hash_eq_other_ver = [ # Git versions and literal versions are distinct versions, like
Spec(x.format(**format_info)) for x in specs # pkg@10.1.0 and pkg@10.1.0-suffix are distinct versions.
] assert not hash_eq_ver.satisfies(just_ver)
assert hash_eq_ver.satisfies(just_hash)
assert not just_hash.satisfies(hash_eq_ver)
assert hash_eq_ver.satisfies(just_ver)
assert not just_ver.satisfies(hash_eq_ver) assert not just_ver.satisfies(hash_eq_ver)
assert not hash_eq_ver.intersects(just_ver)
assert hash_eq_ver != just_ver
assert just_ver != hash_eq_ver
assert not hash_eq_ver == just_ver
assert not just_ver == hash_eq_ver
# When a different version is associated, they're not equal
assert not hash_eq_ver.satisfies(hash_eq_other_ver) assert not hash_eq_ver.satisfies(hash_eq_other_ver)
assert not hash_eq_other_ver.satisfies(hash_eq_ver) assert not hash_eq_other_ver.satisfies(hash_eq_ver)
assert not hash_eq_ver.intersects(hash_eq_other_ver)
assert not hash_eq_other_ver.intersects(hash_eq_ver)
assert hash_eq_ver != hash_eq_other_ver
assert hash_eq_other_ver != hash_eq_ver
assert not hash_eq_ver == hash_eq_other_ver
assert not hash_eq_other_ver == hash_eq_ver
# These should be equal
assert hash_eq_ver == hash_eq_ver_copy
assert not hash_eq_ver != hash_eq_ver_copy
assert hash_eq_ver.satisfies(hash_eq_ver_copy)
assert hash_eq_ver_copy.satisfies(hash_eq_ver)
assert hash_eq_ver.intersects(hash_eq_ver_copy)
assert hash_eq_ver_copy.intersects(hash_eq_ver)
def test_requirement_adds_new_version( def test_requirement_adds_new_version(
concretize_scope, test_repo, mock_git_version_info, monkeypatch concretize_scope, test_repo, mock_git_version_info, monkeypatch
): ):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
repo_path, filename, commits = mock_git_version_info repo_path, filename, commits = mock_git_version_info
monkeypatch.setattr( monkeypatch.setattr(
@ -189,7 +214,6 @@ def test_requirement_adds_new_version(
s1 = Spec("v").concretized() s1 = Spec("v").concretized()
assert s1.satisfies("@2.2") assert s1.satisfies("@2.2")
assert s1.satisfies("@{0}".format(a_commit_hash))
# Make sure the git commit info is retained # Make sure the git commit info is retained
assert isinstance(s1.version, spack.version.GitVersion) assert isinstance(s1.version, spack.version.GitVersion)
assert s1.version.ref == a_commit_hash assert s1.version.ref == a_commit_hash
@ -199,7 +223,7 @@ def test_requirement_adds_git_hash_version(
concretize_scope, test_repo, mock_git_version_info, monkeypatch concretize_scope, test_repo, mock_git_version_info, monkeypatch
): ):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
repo_path, filename, commits = mock_git_version_info repo_path, filename, commits = mock_git_version_info
monkeypatch.setattr( monkeypatch.setattr(
@ -207,46 +231,39 @@ def test_requirement_adds_git_hash_version(
) )
a_commit_hash = commits[0] a_commit_hash = commits[0]
conf_str = """\ conf_str = f"""\
packages: packages:
v: v:
require: "@{0}" require: "@{a_commit_hash}"
""".format( """
a_commit_hash
)
update_packages_config(conf_str) update_packages_config(conf_str)
s1 = Spec("v").concretized() s1 = Spec("v").concretized()
assert s1.satisfies("@{0}".format(a_commit_hash)) assert isinstance(s1.version, spack.version.GitVersion)
assert s1.satisfies(f"v@{a_commit_hash}")
def test_requirement_adds_multiple_new_versions( def test_requirement_adds_multiple_new_versions(
concretize_scope, test_repo, mock_git_version_info, monkeypatch concretize_scope, test_repo, mock_git_version_info, monkeypatch
): ):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
repo_path, filename, commits = mock_git_version_info repo_path, filename, commits = mock_git_version_info
monkeypatch.setattr( monkeypatch.setattr(
spack.package_base.PackageBase, "git", path_to_file_url(repo_path), raising=False spack.package_base.PackageBase, "git", path_to_file_url(repo_path), raising=False
) )
conf_str = """\ conf_str = f"""\
packages: packages:
v: v:
require: require:
- one_of: ["@{0}=2.2", "@{1}=2.3"] - one_of: ["@{commits[0]}=2.2", "@{commits[1]}=2.3"]
""".format( """
commits[0], commits[1]
)
update_packages_config(conf_str) update_packages_config(conf_str)
s1 = Spec("v").concretized() assert Spec("v").concretized().satisfies(f"@{commits[0]}=2.2")
assert s1.satisfies("@2.2") assert Spec("v@2.3").concretized().satisfies(f"v@{commits[1]}=2.3")
s2 = Spec("v@{0}".format(commits[1])).concretized()
assert s2.satisfies("@{0}".format(commits[1]))
assert s2.satisfies("@2.3")
# TODO: this belongs in the concretize_preferences test module but uses # TODO: this belongs in the concretize_preferences test module but uses
@ -255,35 +272,27 @@ def test_preference_adds_new_version(
concretize_scope, test_repo, mock_git_version_info, monkeypatch concretize_scope, test_repo, mock_git_version_info, monkeypatch
): ):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
repo_path, filename, commits = mock_git_version_info repo_path, filename, commits = mock_git_version_info
monkeypatch.setattr( monkeypatch.setattr(
spack.package_base.PackageBase, "git", path_to_file_url(repo_path), raising=False spack.package_base.PackageBase, "git", path_to_file_url(repo_path), raising=False
) )
conf_str = """\ conf_str = f"""\
packages: packages:
v: v:
version: ["{0}=2.2", "{1}=2.3"] version: ["{commits[0]}=2.2", "{commits[1]}=2.3"]
""".format( """
commits[0], commits[1]
)
update_packages_config(conf_str) update_packages_config(conf_str)
s1 = Spec("v").concretized() assert Spec("v").concretized().satisfies(f"@{commits[0]}=2.2")
assert s1.satisfies("@2.2") assert Spec("v@2.3").concretized().satisfies(f"@{commits[1]}=2.3")
assert s1.satisfies("@{0}".format(commits[0]))
s2 = Spec("v@2.3").concretized() # When installing by hash, a lookup is triggered, so it's not mapped to =2.3.
# Note: this check will fail: the command-line spec version is preferred s3 = Spec(f"v@{commits[1]}").concretized()
# assert s2.satisfies("@{0}".format(commits[1])) assert s3.satisfies(f"v@{commits[1]}")
assert s2.satisfies("@2.3") assert not s3.satisfies("@2.3")
s3 = Spec("v@{0}".format(commits[1])).concretized()
assert s3.satisfies("@{0}".format(commits[1]))
# Note: this check will fail: the command-line spec version is preferred
# assert s3.satisfies("@2.3")
def test_requirement_is_successfully_applied(concretize_scope, test_repo): def test_requirement_is_successfully_applied(concretize_scope, test_repo):
@ -291,7 +300,7 @@ def test_requirement_is_successfully_applied(concretize_scope, test_repo):
concretization succeeds and the requirement spec is applied. concretization succeeds and the requirement spec is applied.
""" """
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
s1 = Spec("x").concretized() s1 = Spec("x").concretized()
# Without any requirements/preferences, the later version is preferred # Without any requirements/preferences, the later version is preferred
@ -313,7 +322,7 @@ def test_multiple_packages_requirements_are_respected(concretize_scope, test_rep
succeeds and both requirements are respected. succeeds and both requirements are respected.
""" """
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
@ -333,7 +342,7 @@ def test_oneof(concretize_scope, test_repo):
the specs in the group (but not all have to be satisfied). the specs in the group (but not all have to be satisfied).
""" """
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
@ -353,7 +362,7 @@ def test_one_package_multiple_oneof_groups(concretize_scope, test_repo):
applied. applied.
""" """
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
@ -377,7 +386,7 @@ def test_requirements_for_package_that_is_not_needed(concretize_scope, test_repo
the requirements are used for the requested spec). the requirements are used for the requested spec).
""" """
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
# Note that the exact contents aren't important since this isn't # Note that the exact contents aren't important since this isn't
# intended to be used, but the important thing is that a number of # intended to be used, but the important thing is that a number of
@ -403,7 +412,7 @@ def test_oneof_ordering(concretize_scope, test_repo):
later versions). later versions).
""" """
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
@ -422,7 +431,7 @@ def test_oneof_ordering(concretize_scope, test_repo):
def test_reuse_oneof(concretize_scope, create_test_repo, mutable_database, fake_installs): def test_reuse_oneof(concretize_scope, create_test_repo, mutable_database, fake_installs):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
@ -445,7 +454,7 @@ def test_reuse_oneof(concretize_scope, create_test_repo, mutable_database, fake_
def test_requirements_are_higher_priority_than_deprecation(concretize_scope, test_repo): def test_requirements_are_higher_priority_than_deprecation(concretize_scope, test_repo):
"""Test that users can override a deprecated version with a requirement.""" """Test that users can override a deprecated version with a requirement."""
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
# @2.3 is a deprecated versions. Ensure that any_of picks both constraints, # @2.3 is a deprecated versions. Ensure that any_of picks both constraints,
# since they are possible # since they are possible
@ -466,7 +475,7 @@ def test_requirements_are_higher_priority_than_deprecation(concretize_scope, tes
def test_default_requirements_with_all(spec_str, requirement_str, concretize_scope, test_repo): def test_default_requirements_with_all(spec_str, requirement_str, concretize_scope, test_repo):
"""Test that default requirements are applied to all packages.""" """Test that default requirements are applied to all packages."""
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
@ -494,7 +503,7 @@ def test_default_and_package_specific_requirements(
): ):
"""Test that specific package requirements override default package requirements.""" """Test that specific package requirements override default package requirements."""
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
generic_req, specific_req = requirements generic_req, specific_req = requirements
generic_exp, specific_exp = expectations generic_exp, specific_exp = expectations
conf_str = """\ conf_str = """\
@ -517,7 +526,7 @@ def test_default_and_package_specific_requirements(
@pytest.mark.parametrize("mpi_requirement", ["mpich", "mpich2", "zmpi"]) @pytest.mark.parametrize("mpi_requirement", ["mpich", "mpich2", "zmpi"])
def test_requirements_on_virtual(mpi_requirement, concretize_scope, mock_packages): def test_requirements_on_virtual(mpi_requirement, concretize_scope, mock_packages):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
mpi: mpi:
@ -540,7 +549,7 @@ def test_requirements_on_virtual_and_on_package(
mpi_requirement, specific_requirement, concretize_scope, mock_packages mpi_requirement, specific_requirement, concretize_scope, mock_packages
): ):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
mpi: mpi:
@ -560,7 +569,7 @@ def test_requirements_on_virtual_and_on_package(
def test_incompatible_virtual_requirements_raise(concretize_scope, mock_packages): def test_incompatible_virtual_requirements_raise(concretize_scope, mock_packages):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
mpi: mpi:
@ -575,7 +584,7 @@ def test_incompatible_virtual_requirements_raise(concretize_scope, mock_packages
def test_non_existing_variants_under_all(concretize_scope, mock_packages): def test_non_existing_variants_under_all(concretize_scope, mock_packages):
if spack.config.get("config:concretizer") == "original": if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements") pytest.skip("Original concretizer does not support configuration requirements")
conf_str = """\ conf_str = """\
packages: packages:
all: all:

View file

@ -123,7 +123,7 @@ def mock_git_version_info(git, tmpdir, override_git_repos_cache_path):
o second commit (v1.0) o second commit (v1.0)
o first commit o first commit
The repo consists of a single file, in which the Version._cmp representation The repo consists of a single file, in which the GitVersion._ref_version representation
of each commit is expressed as a string. of each commit is expressed as a string.
Important attributes of the repo for test coverage are: multiple branches, Important attributes of the repo for test coverage are: multiple branches,
@ -175,7 +175,7 @@ def latest_commit():
# Add two commits and a tag on 1.x branch # Add two commits and a tag on 1.x branch
git("checkout", "-b", "1.x") git("checkout", "-b", "1.x")
write_file(filename, "[1, 0, '', 1]") write_file(filename, "[1, 0, 'git', 1]")
commit("first 1.x commit") commit("first 1.x commit")
commits.append(latest_commit()) commits.append(latest_commit())
@ -186,7 +186,7 @@ def latest_commit():
# Add two commits and a tag on main branch # Add two commits and a tag on main branch
git("checkout", main) git("checkout", main)
write_file(filename, "[1, 0, '', 1]") write_file(filename, "[1, 0, 'git', 1]")
commit("third main commit") commit("third main commit")
commits.append(latest_commit()) commits.append(latest_commit())
write_file(filename, "[2, 0]") write_file(filename, "[2, 0]")
@ -196,7 +196,7 @@ def latest_commit():
# Add two more commits on 1.x branch to ensure we aren't cheating by using time # Add two more commits on 1.x branch to ensure we aren't cheating by using time
git("checkout", "1.x") git("checkout", "1.x")
write_file(filename, "[1, 1, '', 1]") write_file(filename, "[1, 1, 'git', 1]")
commit("third 1.x commit") commit("third 1.x commit")
commits.append(latest_commit()) commits.append(latest_commit())
write_file(filename, "[1, 2]") write_file(filename, "[1, 2]")

View file

@ -15,7 +15,12 @@
import pytest import pytest
import spack import spack
import spack.cmd
import spack.compilers
import spack.config
import spack.cray_manifest as cray_manifest import spack.cray_manifest as cray_manifest
import spack.spec
import spack.store
from spack.cray_manifest import compiler_from_entry, entries_to_specs from spack.cray_manifest import compiler_from_entry, entries_to_specs
example_x_json_str = """\ example_x_json_str = """\
@ -348,7 +353,7 @@ def test_read_cray_manifest_twice_no_compiler_duplicates(
): ):
if spack.config.get("config:concretizer") == "clingo": if spack.config.get("config:concretizer") == "clingo":
pytest.skip( pytest.skip(
"The ASP-based concretizer is currently picky about " " OS matching and will fail." "The ASP-based concretizer is currently picky about OS matching and will fail."
) )
with tmpdir.as_cwd(): with tmpdir.as_cwd():
@ -362,7 +367,7 @@ def test_read_cray_manifest_twice_no_compiler_duplicates(
compilers = spack.compilers.all_compilers() compilers = spack.compilers.all_compilers()
filtered = list( filtered = list(
c for c in compilers if c.spec == spack.spec.CompilerSpec("gcc@10.2.0.cray") c for c in compilers if c.spec == spack.spec.CompilerSpec("gcc@=10.2.0.cray")
) )
assert len(filtered) == 1 assert len(filtered) == 1

View file

@ -13,7 +13,7 @@
from spack.spec import Spec from spack.spec import Spec
from spack.stage import Stage from spack.stage import Stage
from spack.util.executable import which from spack.util.executable import which
from spack.version import ver from spack.version import Version
pytestmark = pytest.mark.skipif(not which("cvs"), reason="requires CVS to be installed") pytestmark = pytest.mark.skipif(not which("cvs"), reason="requires CVS to be installed")
@ -39,7 +39,7 @@ def test_fetch(type_of_test, mock_cvs_repository, config, mutable_mock_repo):
# Construct the package under test # Construct the package under test
spec = Spec("cvs-test").concretized() spec = Spec("cvs-test").concretized()
spec.package.versions[ver("cvs")] = test.args spec.package.versions[Version("cvs")] = test.args
# Enter the stage directory and check some properties # Enter the stage directory and check some properties
with spec.package.stage: with spec.package.stage:

View file

@ -2,11 +2,14 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
from collections import namedtuple
import pytest import pytest
import spack.directives import spack.directives
import spack.repo import spack.repo
import spack.spec import spack.spec
import spack.version
def test_false_directives_do_not_exist(mock_packages): def test_false_directives_do_not_exist(mock_packages):
@ -84,3 +87,20 @@ def test_error_on_anonymous_dependency(config, mock_packages):
def test_maintainer_directive(config, mock_packages, package_name, expected_maintainers): def test_maintainer_directive(config, mock_packages, package_name, expected_maintainers):
pkg_cls = spack.repo.path.get_pkg_class(package_name) pkg_cls = spack.repo.path.get_pkg_class(package_name)
assert pkg_cls.maintainers == expected_maintainers assert pkg_cls.maintainers == expected_maintainers
def test_version_type_validation():
# A version should be a string or an int, not a float, because it leads to subtle issues
# such as 3.10 being interpreted as 3.1.
package = namedtuple("package", ["name"])
msg = r"python: declared version '.+' in package should be a string or int\."
# Pass a float
with pytest.raises(spack.version.VersionError, match=msg):
spack.directives._execute_version(package(name="python"), 3.10)
# Try passing a bogus type; it's just that we want a nice error message
with pytest.raises(spack.version.VersionError, match=msg):
spack.directives._execute_version(package(name="python"), {})

View file

@ -16,7 +16,7 @@
from spack.fetch_strategy import GitFetchStrategy from spack.fetch_strategy import GitFetchStrategy
from spack.spec import Spec from spack.spec import Spec
from spack.stage import Stage from spack.stage import Stage
from spack.version import ver from spack.version import Version
_mock_transport_error = "Mock HTTP transport error" _mock_transport_error = "Mock HTTP transport error"
@ -36,7 +36,7 @@ def git_version(git, request, monkeypatch):
# Don't patch; run with the real git_version method. # Don't patch; run with the real git_version method.
yield real_git_version yield real_git_version
else: else:
test_git_version = ver(request.param) test_git_version = Version(request.param)
if test_git_version > real_git_version: if test_git_version > real_git_version:
pytest.skip("Can't test clone logic for newer version of git.") pytest.skip("Can't test clone logic for newer version of git.")
@ -61,7 +61,7 @@ def bad_git(*args, **kwargs):
# Patch the fetch strategy to think it's using a git version that # Patch the fetch strategy to think it's using a git version that
# will error out when git is called. # will error out when git is called.
monkeypatch.setattr(GitFetchStrategy, "git", bad_git) monkeypatch.setattr(GitFetchStrategy, "git", bad_git)
monkeypatch.setattr(GitFetchStrategy, "git_version", ver("1.7.1")) monkeypatch.setattr(GitFetchStrategy, "git_version", Version("1.7.1"))
yield yield
@ -107,7 +107,7 @@ def test_fetch(
# Construct the package under test # Construct the package under test
s = default_mock_concretization("git-test") s = default_mock_concretization("git-test")
monkeypatch.setitem(s.package.versions, ver("git"), t.args) monkeypatch.setitem(s.package.versions, Version("git"), t.args)
# Enter the stage directory and check some properties # Enter the stage directory and check some properties
with s.package.stage: with s.package.stage:
@ -154,7 +154,7 @@ def test_fetch_pkg_attr_submodule_init(
# Construct the package under test # Construct the package under test
s = default_mock_concretization("git-test") s = default_mock_concretization("git-test")
monkeypatch.setitem(s.package.versions, ver("git"), t.args) monkeypatch.setitem(s.package.versions, Version("git"), t.args)
s.package.do_stage() s.package.do_stage()
collected_fnames = set() collected_fnames = set()
@ -180,7 +180,7 @@ def test_adhoc_version_submodules(
t = mock_git_repository.checks["tag"] t = mock_git_repository.checks["tag"]
# Construct the package under test # Construct the package under test
pkg_class = spack.repo.path.get_pkg_class("git-test") pkg_class = spack.repo.path.get_pkg_class("git-test")
monkeypatch.setitem(pkg_class.versions, ver("git"), t.args) monkeypatch.setitem(pkg_class.versions, Version("git"), t.args)
monkeypatch.setattr(pkg_class, "git", "file://%s" % mock_git_repository.path, raising=False) monkeypatch.setattr(pkg_class, "git", "file://%s" % mock_git_repository.path, raising=False)
spec = Spec("git-test@{0}".format(mock_git_repository.unversioned_commit)) spec = Spec("git-test@{0}".format(mock_git_repository.unversioned_commit))
@ -203,7 +203,7 @@ def test_debug_fetch(
# Construct the package under test # Construct the package under test
s = default_mock_concretization("git-test") s = default_mock_concretization("git-test")
monkeypatch.setitem(s.package.versions, ver("git"), t.args) monkeypatch.setitem(s.package.versions, Version("git"), t.args)
# Fetch then ensure source path exists # Fetch then ensure source path exists
with s.package.stage: with s.package.stage:
@ -243,7 +243,7 @@ def test_get_full_repo(
): ):
"""Ensure that we can clone a full repository.""" """Ensure that we can clone a full repository."""
if git_version < ver("1.7.1"): if git_version < Version("1.7.1"):
pytest.skip("Not testing get_full_repo for older git {0}".format(git_version)) pytest.skip("Not testing get_full_repo for older git {0}".format(git_version))
secure = True secure = True
@ -254,7 +254,7 @@ def test_get_full_repo(
s = default_mock_concretization("git-test") s = default_mock_concretization("git-test")
args = copy.copy(t.args) args = copy.copy(t.args)
args["get_full_repo"] = get_full_repo args["get_full_repo"] = get_full_repo
monkeypatch.setitem(s.package.versions, ver("git"), args) monkeypatch.setitem(s.package.versions, Version("git"), args)
with s.package.stage: with s.package.stage:
with spack.config.override("config:verify_ssl", secure): with spack.config.override("config:verify_ssl", secure):
@ -299,7 +299,7 @@ def test_gitsubmodule(
s = default_mock_concretization("git-test") s = default_mock_concretization("git-test")
args = copy.copy(t.args) args = copy.copy(t.args)
args["submodules"] = submodules args["submodules"] = submodules
monkeypatch.setitem(s.package.versions, ver("git"), args) monkeypatch.setitem(s.package.versions, Version("git"), args)
s.package.do_stage() s.package.do_stage()
with working_dir(s.package.stage.source_path): with working_dir(s.package.stage.source_path):
for submodule_count in range(2): for submodule_count in range(2):
@ -332,7 +332,7 @@ def submodules_callback(package):
s = default_mock_concretization("git-test") s = default_mock_concretization("git-test")
args = copy.copy(t.args) args = copy.copy(t.args)
args["submodules"] = submodules_callback args["submodules"] = submodules_callback
monkeypatch.setitem(s.package.versions, ver("git"), args) monkeypatch.setitem(s.package.versions, Version("git"), args)
s.package.do_stage() s.package.do_stage()
with working_dir(s.package.stage.source_path): with working_dir(s.package.stage.source_path):
file_path = os.path.join(s.package.stage.source_path, "third_party/submodule0/r0_file_0") file_path = os.path.join(s.package.stage.source_path, "third_party/submodule0/r0_file_0")
@ -356,7 +356,7 @@ def test_gitsubmodules_delete(
args = copy.copy(t.args) args = copy.copy(t.args)
args["submodules"] = True args["submodules"] = True
args["submodules_delete"] = ["third_party/submodule0", "third_party/submodule1"] args["submodules_delete"] = ["third_party/submodule0", "third_party/submodule1"]
monkeypatch.setitem(s.package.versions, ver("git"), args) monkeypatch.setitem(s.package.versions, Version("git"), args)
s.package.do_stage() s.package.do_stage()
with working_dir(s.package.stage.source_path): with working_dir(s.package.stage.source_path):
file_path = os.path.join(s.package.stage.source_path, "third_party/submodule0") file_path = os.path.join(s.package.stage.source_path, "third_party/submodule0")

View file

@ -16,7 +16,7 @@
from spack.spec import Spec from spack.spec import Spec
from spack.stage import Stage from spack.stage import Stage
from spack.util.executable import which from spack.util.executable import which
from spack.version import ver from spack.version import Version
# Test functionality covered is supported on Windows, but currently failing # Test functionality covered is supported on Windows, but currently failing
# and expected to be fixed # and expected to be fixed
@ -44,7 +44,7 @@ def test_fetch(type_of_test, secure, mock_hg_repository, config, mutable_mock_re
# Construct the package under test # Construct the package under test
s = Spec("hg-test").concretized() s = Spec("hg-test").concretized()
monkeypatch.setitem(s.package.versions, ver("hg"), t.args) monkeypatch.setitem(s.package.versions, Version("hg"), t.args)
# Enter the stage directory and check some properties # Enter the stage directory and check some properties
with s.package.stage: with s.package.stage:

View file

@ -17,12 +17,15 @@
import spack.binary_distribution import spack.binary_distribution
import spack.compilers import spack.compilers
import spack.concretize
import spack.config
import spack.installer as inst import spack.installer as inst
import spack.package_prefs as prefs import spack.package_prefs as prefs
import spack.repo import spack.repo
import spack.spec import spack.spec
import spack.store import spack.store
import spack.util.lock as lk import spack.util.lock as lk
import spack.version
def _mock_repo(root, namespace): def _mock_repo(root, namespace):
@ -528,10 +531,12 @@ def test_bootstrapping_compilers_with_different_names_from_spec(
): ):
with spack.config.override("config:install_missing_compilers", True): with spack.config.override("config:install_missing_compilers", True):
with spack.concretize.disable_compiler_existence_check(): with spack.concretize.disable_compiler_existence_check():
spec = spack.spec.Spec("trivial-install-test-package%oneapi@22.2.0").concretized() spec = spack.spec.Spec("trivial-install-test-package%oneapi@=22.2.0").concretized()
spec.package.do_install() spec.package.do_install()
assert spack.spec.CompilerSpec("oneapi@22.2.0") in spack.compilers.all_compiler_specs() assert (
spack.spec.CompilerSpec("oneapi@=22.2.0") in spack.compilers.all_compiler_specs()
)
def test_dump_packages_deps_ok(install_mockery, tmpdir, mock_packages): def test_dump_packages_deps_ok(install_mockery, tmpdir, mock_packages):

View file

@ -197,7 +197,7 @@ def test_invalid_json_mirror_collection(invalid_json, error_message):
def test_mirror_archive_paths_no_version(mock_packages, config, mock_archive): def test_mirror_archive_paths_no_version(mock_packages, config, mock_archive):
spec = Spec("trivial-install-test-package@nonexistingversion").concretized() spec = Spec("trivial-install-test-package@=nonexistingversion").concretized()
fetcher = spack.fetch_strategy.URLFetchStrategy(mock_archive.url) fetcher = spack.fetch_strategy.URLFetchStrategy(mock_archive.url)
spack.mirror.mirror_archive_paths(fetcher, "per-package-ref", spec) spack.mirror.mirror_archive_paths(fetcher, "per-package-ref", spec)
@ -281,8 +281,8 @@ def test_mirror_cache_symlinks(tmpdir):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"specs,expected_specs", "specs,expected_specs",
[ [
(["a"], ["a@1.0", "a@2.0"]), (["a"], ["a@=1.0", "a@=2.0"]),
(["a", "brillig"], ["a@1.0", "a@2.0", "brillig@1.0.0", "brillig@2.0.0"]), (["a", "brillig"], ["a@=1.0", "a@=2.0", "brillig@=1.0.0", "brillig@=2.0.0"]),
], ],
) )
def test_get_all_versions(specs, expected_specs): def test_get_all_versions(specs, expected_specs):

View file

@ -24,7 +24,7 @@
pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")
@pytest.fixture(params=["clang@12.0.0", "gcc@10.2.1"]) @pytest.fixture(params=["clang@=12.0.0", "gcc@=10.2.1"])
def compiler(request): def compiler(request):
return request.param return request.param
@ -61,10 +61,10 @@ def test_file_layout(self, compiler, provider, factory, module_configuration):
# is transformed to r"Core" if the compiler is listed among core # is transformed to r"Core" if the compiler is listed among core
# compilers # compilers
# Check that specs listed as core_specs are transformed to "Core" # Check that specs listed as core_specs are transformed to "Core"
if compiler == "clang@3.3" or spec_string == "mpich@3.0.1": if compiler == "clang@=3.3" or spec_string == "mpich@3.0.1":
assert "Core" in layout.available_path_parts assert "Core" in layout.available_path_parts
else: else:
assert compiler.replace("@", "/") in layout.available_path_parts assert compiler.replace("@=", "/") in layout.available_path_parts
# Check that the provider part instead has always an hash even if # Check that the provider part instead has always an hash even if
# hash has been disallowed in the configuration file # hash has been disallowed in the configuration file

View file

@ -54,8 +54,8 @@ def test_no_version_match(pkg_name):
("^mpich2@1.2", "mpi_version", 2), ("^mpich2@1.2", "mpi_version", 2),
("^mpich@1.0", "mpi_version", 1), ("^mpich@1.0", "mpi_version", 1),
# Undefined mpi versions # Undefined mpi versions
("^mpich@0.4", "mpi_version", 1), ("^mpich@=0.4", "mpi_version", 1),
("^mpich@1.4", "mpi_version", 1), ("^mpich@=1.4", "mpi_version", 1),
# Constraints on compilers with a default # Constraints on compilers with a default
("%gcc", "has_a_default", "gcc"), ("%gcc", "has_a_default", "gcc"),
("%clang", "has_a_default", "clang"), ("%clang", "has_a_default", "clang"),
@ -107,11 +107,11 @@ def test_target_match(pkg_name):
("multimethod@2.0", "inherited_and_overridden", "base@2.0"), ("multimethod@2.0", "inherited_and_overridden", "base@2.0"),
# Diamond-like inheritance (even though the MRO linearize everything) # Diamond-like inheritance (even though the MRO linearize everything)
("multimethod-diamond@1.0", "diamond_inheritance", "base_package"), ("multimethod-diamond@1.0", "diamond_inheritance", "base_package"),
("multimethod-base@1.0", "diamond_inheritance", "base_package"), ("multimethod-base@=1.0", "diamond_inheritance", "base_package"),
("multimethod-diamond@2.0", "diamond_inheritance", "first_parent"), ("multimethod-diamond@2.0", "diamond_inheritance", "first_parent"),
("multimethod-inheritor@2.0", "diamond_inheritance", "first_parent"), ("multimethod-inheritor@2.0", "diamond_inheritance", "first_parent"),
("multimethod-diamond@3.0", "diamond_inheritance", "second_parent"), ("multimethod-diamond@=3.0", "diamond_inheritance", "second_parent"),
("multimethod-diamond-parent@3.0", "diamond_inheritance", "second_parent"), ("multimethod-diamond-parent@=3.0", "diamond_inheritance", "second_parent"),
("multimethod-diamond@4.0", "diamond_inheritance", "subclass"), ("multimethod-diamond@4.0", "diamond_inheritance", "subclass"),
], ],
) )

View file

@ -148,7 +148,7 @@ def test_patch_mixed_versions_subset_constraint(mock_packages, config):
spec1.concretize() spec1.concretize()
assert biz_sha256 in spec1.variants["patches"].value assert biz_sha256 in spec1.variants["patches"].value
spec2 = Spec("patch@1.0") spec2 = Spec("patch@=1.0")
spec2.concretize() spec2.concretize()
assert biz_sha256 not in spec2.variants["patches"].value assert biz_sha256 not in spec2.variants["patches"].value

View file

@ -986,8 +986,8 @@ def test_synthetic_construction_of_split_dependencies_from_same_package(mock_pac
# To demonstrate that a spec can now hold two direct # To demonstrate that a spec can now hold two direct
# dependencies from the same package # dependencies from the same package
root = Spec("b").concretized() root = Spec("b").concretized()
link_run_spec = Spec("c@1.0").concretized() link_run_spec = Spec("c@=1.0").concretized()
build_spec = Spec("c@2.0").concretized() build_spec = Spec("c@=2.0").concretized()
root.add_dependency_edge(link_run_spec, deptypes="link") root.add_dependency_edge(link_run_spec, deptypes="link")
root.add_dependency_edge(link_run_spec, deptypes="run") root.add_dependency_edge(link_run_spec, deptypes="run")
@ -1014,8 +1014,8 @@ def test_synthetic_construction_bootstrapping(mock_packages, config):
# | build # | build
# b@1.0 # b@1.0
# #
root = Spec("b@2.0").concretized() root = Spec("b@=2.0").concretized()
bootstrap = Spec("b@1.0").concretized() bootstrap = Spec("b@=1.0").concretized()
root.add_dependency_edge(bootstrap, deptypes="build") root.add_dependency_edge(bootstrap, deptypes="build")
@ -1032,8 +1032,8 @@ def test_addition_of_different_deptypes_in_multiple_calls(mock_packages, config)
# b@1.0 # b@1.0
# #
# with three calls and check we always have a single edge # with three calls and check we always have a single edge
root = Spec("b@2.0").concretized() root = Spec("b@=2.0").concretized()
bootstrap = Spec("b@1.0").concretized() bootstrap = Spec("b@=1.0").concretized()
for current_deptype in ("build", "link", "run"): for current_deptype in ("build", "link", "run"):
root.add_dependency_edge(bootstrap, deptypes=current_deptype) root.add_dependency_edge(bootstrap, deptypes=current_deptype)
@ -1059,9 +1059,9 @@ def test_addition_of_different_deptypes_in_multiple_calls(mock_packages, config)
def test_adding_same_deptype_with_the_same_name_raises( def test_adding_same_deptype_with_the_same_name_raises(
mock_packages, config, c1_deptypes, c2_deptypes mock_packages, config, c1_deptypes, c2_deptypes
): ):
p = Spec("b@2.0").concretized() p = Spec("b@=2.0").concretized()
c1 = Spec("b@1.0").concretized() c1 = Spec("b@=1.0").concretized()
c2 = Spec("b@2.0").concretized() c2 = Spec("b@=2.0").concretized()
p.add_dependency_edge(c1, deptypes=c1_deptypes) p.add_dependency_edge(c1, deptypes=c1_deptypes)
with pytest.raises(spack.error.SpackError): with pytest.raises(spack.error.SpackError):

View file

@ -628,16 +628,16 @@ def test_spec_formatting(self, default_mock_concretization):
# component: subcomponent of spec from which to get property # component: subcomponent of spec from which to get property
package_segments = [ package_segments = [
("{NAME}", "", "name", lambda spec: spec), ("{NAME}", "", "name", lambda spec: spec),
("{VERSION}", "", "versions", lambda spec: spec), ("{VERSION}", "", "version", lambda spec: spec),
("{compiler}", "", "compiler", lambda spec: spec), ("{compiler}", "", "compiler", lambda spec: spec),
("{compiler_flags}", "", "compiler_flags", lambda spec: spec), ("{compiler_flags}", "", "compiler_flags", lambda spec: spec),
("{variants}", "", "variants", lambda spec: spec), ("{variants}", "", "variants", lambda spec: spec),
("{architecture}", "", "architecture", lambda spec: spec), ("{architecture}", "", "architecture", lambda spec: spec),
("{@VERSIONS}", "@", "version", lambda spec: spec), ("{@VERSIONS}", "@", "versions", lambda spec: spec),
("{%compiler}", "%", "compiler", lambda spec: spec), ("{%compiler}", "%", "compiler", lambda spec: spec),
("{arch=architecture}", "arch=", "architecture", lambda spec: spec), ("{arch=architecture}", "arch=", "architecture", lambda spec: spec),
("{compiler.name}", "", "name", lambda spec: spec.compiler), ("{compiler.name}", "", "name", lambda spec: spec.compiler),
("{compiler.version}", "", "versions", lambda spec: spec.compiler), ("{compiler.version}", "", "version", lambda spec: spec.compiler),
("{%compiler.name}", "%", "name", lambda spec: spec.compiler), ("{%compiler.name}", "%", "name", lambda spec: spec.compiler),
("{@compiler.version}", "@", "version", lambda spec: spec.compiler), ("{@compiler.version}", "@", "version", lambda spec: spec.compiler),
("{architecture.platform}", "", "platform", lambda spec: spec.architecture), ("{architecture.platform}", "", "platform", lambda spec: spec.architecture),

View file

@ -662,7 +662,13 @@ def test_dep_spec_by_hash(database):
).next_spec() ).next_spec()
assert "zmpi" in mpileaks_hash_zmpi assert "zmpi" in mpileaks_hash_zmpi
assert mpileaks_hash_zmpi["zmpi"] == zmpi assert mpileaks_hash_zmpi["zmpi"] == zmpi
assert mpileaks_hash_zmpi.compiler == mpileaks_zmpi.compiler
# notice: the round-trip str -> Spec loses specificity when
# since %gcc@=x gets printed as %gcc@x. So stick to satisfies
# here, unless/until we want to differentiate between ranges
# and specific versions in the future.
# assert mpileaks_hash_zmpi.compiler == mpileaks_zmpi.compiler
assert mpileaks_zmpi.compiler.satisfies(mpileaks_hash_zmpi.compiler)
mpileaks_hash_fake_and_zmpi = SpecParser( mpileaks_hash_fake_and_zmpi = SpecParser(
f"mpileaks ^/{fake.dag_hash()[:4]} ^ /{zmpi.dag_hash()[:5]}" f"mpileaks ^/{fake.dag_hash()[:4]} ^ /{zmpi.dag_hash()[:5]}"

View file

@ -16,7 +16,7 @@
from spack.spec import Spec from spack.spec import Spec
from spack.stage import Stage from spack.stage import Stage
from spack.util.executable import which from spack.util.executable import which
from spack.version import ver from spack.version import Version
pytestmark = [ pytestmark = [
pytest.mark.skipif( pytest.mark.skipif(
@ -44,7 +44,7 @@ def test_fetch(type_of_test, secure, mock_svn_repository, config, mutable_mock_r
# Construct the package under test # Construct the package under test
s = Spec("svn-test").concretized() s = Spec("svn-test").concretized()
monkeypatch.setitem(s.package.versions, ver("svn"), t.args) monkeypatch.setitem(s.package.versions, Version("svn"), t.args)
# Enter the stage directory and check some properties # Enter the stage directory and check some properties
with s.package.stage: with s.package.stage:

View file

@ -21,7 +21,6 @@
from spack.spec import Spec from spack.spec import Spec
from spack.stage import Stage from spack.stage import Stage
from spack.util.executable import which from spack.util.executable import which
from spack.version import ver
@pytest.fixture(params=list(crypto.hashes.keys())) @pytest.fixture(params=list(crypto.hashes.keys()))
@ -153,7 +152,10 @@ def test_fetch(
# Get a spec and tweak the test package with new checksum params # Get a spec and tweak the test package with new checksum params
s = default_mock_concretization("url-test") s = default_mock_concretization("url-test")
s.package.url = mock_archive.url s.package.url = mock_archive.url
s.package.versions[ver("test")] = {checksum_type: checksum, "url": s.package.url} s.package.versions[spack.version.Version("test")] = {
checksum_type: checksum,
"url": s.package.url,
}
# Enter the stage directory and check some properties # Enter the stage directory and check some properties
with s.package.stage: with s.package.stage:
@ -175,13 +177,13 @@ def test_fetch(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"spec,url,digest", "spec,url,digest",
[ [
("url-list-test @0.0.0", "foo-0.0.0.tar.gz", "00000000000000000000000000000000"), ("url-list-test @=0.0.0", "foo-0.0.0.tar.gz", "00000000000000000000000000000000"),
("url-list-test @1.0.0", "foo-1.0.0.tar.gz", "00000000000000000000000000000100"), ("url-list-test @=1.0.0", "foo-1.0.0.tar.gz", "00000000000000000000000000000100"),
("url-list-test @3.0", "foo-3.0.tar.gz", "00000000000000000000000000000030"), ("url-list-test @=3.0", "foo-3.0.tar.gz", "00000000000000000000000000000030"),
("url-list-test @4.5", "foo-4.5.tar.gz", "00000000000000000000000000000450"), ("url-list-test @=4.5", "foo-4.5.tar.gz", "00000000000000000000000000000450"),
("url-list-test @2.0.0b2", "foo-2.0.0b2.tar.gz", "000000000000000000000000000200b2"), ("url-list-test @=2.0.0b2", "foo-2.0.0b2.tar.gz", "000000000000000000000000000200b2"),
("url-list-test @3.0a1", "foo-3.0a1.tar.gz", "000000000000000000000000000030a1"), ("url-list-test @=3.0a1", "foo-3.0a1.tar.gz", "000000000000000000000000000030a1"),
("url-list-test @4.5-rc5", "foo-4.5-rc5.tar.gz", "000000000000000000000000000045c5"), ("url-list-test @=4.5-rc5", "foo-4.5-rc5.tar.gz", "000000000000000000000000000045c5"),
], ],
) )
@pytest.mark.parametrize("_fetch_method", ["curl", "urllib"]) @pytest.mark.parametrize("_fetch_method", ["curl", "urllib"])
@ -209,7 +211,7 @@ def test_from_list_url(mock_packages, config, spec, url, digest, _fetch_method):
[ [
# This version is in the web data path (test/data/web/4.html), but not in the # This version is in the web data path (test/data/web/4.html), but not in the
# url-list-test package. We expect Spack to generate a URL with the new version. # url-list-test package. We expect Spack to generate a URL with the new version.
("4.5.0", "foo-4.5.0.tar.gz", None), ("=4.5.0", "foo-4.5.0.tar.gz", None),
# This version is in web data path and not in the package file, BUT the 2.0.0b2 # This version is in web data path and not in the package file, BUT the 2.0.0b2
# version in the package file satisfies 2.0.0, so Spack will use the known version. # version in the package file satisfies 2.0.0, so Spack will use the known version.
# TODO: this is *probably* not what the user wants, but it's here as an example # TODO: this is *probably* not what the user wants, but it's here as an example

View file

@ -40,29 +40,26 @@ def compare_hash_sans_name(eq, spec1, spec2):
content2 = content2.replace(pkg_cls2.__name__, "TestPackage") content2 = content2.replace(pkg_cls2.__name__, "TestPackage")
hash2 = pkg_cls2(spec2).content_hash(content=content2) hash2 = pkg_cls2(spec2).content_hash(content=content2)
if eq: assert (hash1 == hash2) == eq
assert hash1 == hash2
else:
assert hash1 != hash2
def test_hash(mock_packages, config): def test_hash(mock_packages, config):
ph.package_hash(Spec("hash-test1@1.2")) ph.package_hash(Spec("hash-test1@=1.2"))
def test_different_variants(mock_packages, config): def test_different_variants(mock_packages, config):
spec1 = Spec("hash-test1@1.2 +variantx") spec1 = Spec("hash-test1@=1.2 +variantx")
spec2 = Spec("hash-test1@1.2 +varianty") spec2 = Spec("hash-test1@=1.2 +varianty")
assert ph.package_hash(spec1) == ph.package_hash(spec2) assert ph.package_hash(spec1) == ph.package_hash(spec2)
def test_all_same_but_name(mock_packages, config): def test_all_same_but_name(mock_packages, config):
spec1 = Spec("hash-test1@1.2") spec1 = Spec("hash-test1@=1.2")
spec2 = Spec("hash-test2@1.2") spec2 = Spec("hash-test2@=1.2")
compare_sans_name(True, spec1, spec2) compare_sans_name(True, spec1, spec2)
spec1 = Spec("hash-test1@1.2 +varianty") spec1 = Spec("hash-test1@=1.2 +varianty")
spec2 = Spec("hash-test2@1.2 +varianty") spec2 = Spec("hash-test2@=1.2 +varianty")
compare_sans_name(True, spec1, spec2) compare_sans_name(True, spec1, spec2)
@ -70,26 +67,26 @@ def test_all_same_but_archive_hash(mock_packages, config):
""" """
Archive hash is not intended to be reflected in Package hash. Archive hash is not intended to be reflected in Package hash.
""" """
spec1 = Spec("hash-test1@1.3") spec1 = Spec("hash-test1@=1.3")
spec2 = Spec("hash-test2@1.3") spec2 = Spec("hash-test2@=1.3")
compare_sans_name(True, spec1, spec2) compare_sans_name(True, spec1, spec2)
def test_all_same_but_patch_contents(mock_packages, config): def test_all_same_but_patch_contents(mock_packages, config):
spec1 = Spec("hash-test1@1.1") spec1 = Spec("hash-test1@=1.1")
spec2 = Spec("hash-test2@1.1") spec2 = Spec("hash-test2@=1.1")
compare_sans_name(True, spec1, spec2) compare_sans_name(True, spec1, spec2)
def test_all_same_but_patches_to_apply(mock_packages, config): def test_all_same_but_patches_to_apply(mock_packages, config):
spec1 = Spec("hash-test1@1.4") spec1 = Spec("hash-test1@=1.4")
spec2 = Spec("hash-test2@1.4") spec2 = Spec("hash-test2@=1.4")
compare_sans_name(True, spec1, spec2) compare_sans_name(True, spec1, spec2)
def test_all_same_but_install(mock_packages, config): def test_all_same_but_install(mock_packages, config):
spec1 = Spec("hash-test1@1.5") spec1 = Spec("hash-test1@=1.5")
spec2 = Spec("hash-test2@1.5") spec2 = Spec("hash-test2@=1.5")
compare_sans_name(False, spec1, spec2) compare_sans_name(False, spec1, spec2)
@ -102,14 +99,14 @@ def test_content_hash_all_same_but_patch_contents(mock_packages, config):
def test_content_hash_not_concretized(mock_packages, config): def test_content_hash_not_concretized(mock_packages, config):
"""Check that Package.content_hash() works on abstract specs.""" """Check that Package.content_hash() works on abstract specs."""
# these are different due to the package hash # these are different due to the package hash
spec1 = Spec("hash-test1@1.1") spec1 = Spec("hash-test1@=1.1")
spec2 = Spec("hash-test2@1.3") spec2 = Spec("hash-test2@=1.3")
compare_hash_sans_name(False, spec1, spec2) compare_hash_sans_name(False, spec1, spec2)
# at v1.1 these are actually the same package when @when's are removed # at v1.1 these are actually the same package when @when's are removed
# and the name isn't considered # and the name isn't considered
spec1 = Spec("hash-test1@1.1") spec1 = Spec("hash-test1@=1.1")
spec2 = Spec("hash-test2@1.1") spec2 = Spec("hash-test2@=1.1")
compare_hash_sans_name(True, spec1, spec2) compare_hash_sans_name(True, spec1, spec2)
# these end up being different b/c we can't eliminate much of the package.py # these end up being different b/c we can't eliminate much of the package.py

View file

@ -16,7 +16,16 @@
import spack.package_base import spack.package_base
import spack.spec import spack.spec
from spack.version import GitVersion, Version, VersionBase, VersionList, VersionRange, ver from spack.version import (
GitVersion,
StandardVersion,
Version,
VersionList,
VersionLookupError,
VersionRange,
is_git_version,
ver,
)
def assert_ver_lt(a, b): def assert_ver_lt(a, b):
@ -98,160 +107,160 @@ def check_union(expected, a, b):
def test_string_prefix(): def test_string_prefix():
assert_ver_eq("xsdk-0.2.0", "xsdk-0.2.0") assert_ver_eq("=xsdk-0.2.0", "=xsdk-0.2.0")
assert_ver_lt("xsdk-0.2.0", "xsdk-0.3") assert_ver_lt("=xsdk-0.2.0", "=xsdk-0.3")
assert_ver_gt("xsdk-0.3", "xsdk-0.2.0") assert_ver_gt("=xsdk-0.3", "=xsdk-0.2.0")
def test_two_segments(): def test_two_segments():
assert_ver_eq("1.0", "1.0") assert_ver_eq("=1.0", "=1.0")
assert_ver_lt("1.0", "2.0") assert_ver_lt("=1.0", "=2.0")
assert_ver_gt("2.0", "1.0") assert_ver_gt("=2.0", "=1.0")
def test_develop(): def test_develop():
assert_ver_eq("develop", "develop") assert_ver_eq("=develop", "=develop")
assert_ver_eq("develop.local", "develop.local") assert_ver_eq("=develop.local", "=develop.local")
assert_ver_lt("1.0", "develop") assert_ver_lt("=1.0", "=develop")
assert_ver_gt("develop", "1.0") assert_ver_gt("=develop", "=1.0")
assert_ver_eq("1.develop", "1.develop") assert_ver_eq("=1.develop", "=1.develop")
assert_ver_lt("1.1", "1.develop") assert_ver_lt("=1.1", "=1.develop")
assert_ver_gt("1.develop", "1.0") assert_ver_gt("=1.develop", "=1.0")
assert_ver_gt("0.5.develop", "0.5") assert_ver_gt("=0.5.develop", "=0.5")
assert_ver_lt("0.5", "0.5.develop") assert_ver_lt("=0.5", "=0.5.develop")
assert_ver_lt("1.develop", "2.1") assert_ver_lt("=1.develop", "=2.1")
assert_ver_gt("2.1", "1.develop") assert_ver_gt("=2.1", "=1.develop")
assert_ver_lt("1.develop.1", "1.develop.2") assert_ver_lt("=1.develop.1", "=1.develop.2")
assert_ver_gt("1.develop.2", "1.develop.1") assert_ver_gt("=1.develop.2", "=1.develop.1")
assert_ver_lt("develop.1", "develop.2") assert_ver_lt("=develop.1", "=develop.2")
assert_ver_gt("develop.2", "develop.1") assert_ver_gt("=develop.2", "=develop.1")
# other +infinity versions # other +infinity versions
assert_ver_gt("master", "9.0") assert_ver_gt("=master", "=9.0")
assert_ver_gt("head", "9.0") assert_ver_gt("=head", "=9.0")
assert_ver_gt("trunk", "9.0") assert_ver_gt("=trunk", "=9.0")
assert_ver_gt("develop", "9.0") assert_ver_gt("=develop", "=9.0")
# hierarchical develop-like versions # hierarchical develop-like versions
assert_ver_gt("develop", "master") assert_ver_gt("=develop", "=master")
assert_ver_gt("master", "head") assert_ver_gt("=master", "=head")
assert_ver_gt("head", "trunk") assert_ver_gt("=head", "=trunk")
assert_ver_gt("9.0", "system") assert_ver_gt("=9.0", "=system")
# not develop # not develop
assert_ver_lt("mydevelopmentnightmare", "1.1") assert_ver_lt("=mydevelopmentnightmare", "=1.1")
assert_ver_lt("1.mydevelopmentnightmare", "1.1") assert_ver_lt("=1.mydevelopmentnightmare", "=1.1")
assert_ver_gt("1.1", "1.mydevelopmentnightmare") assert_ver_gt("=1.1", "=1.mydevelopmentnightmare")
def test_isdevelop(): def test_isdevelop():
assert ver("develop").isdevelop() assert ver("=develop").isdevelop()
assert ver("develop.1").isdevelop() assert ver("=develop.1").isdevelop()
assert ver("develop.local").isdevelop() assert ver("=develop.local").isdevelop()
assert ver("master").isdevelop() assert ver("=master").isdevelop()
assert ver("head").isdevelop() assert ver("=head").isdevelop()
assert ver("trunk").isdevelop() assert ver("=trunk").isdevelop()
assert ver("1.develop").isdevelop() assert ver("=1.develop").isdevelop()
assert ver("1.develop.2").isdevelop() assert ver("=1.develop.2").isdevelop()
assert not ver("1.1").isdevelop() assert not ver("=1.1").isdevelop()
assert not ver("1.mydevelopmentnightmare.3").isdevelop() assert not ver("=1.mydevelopmentnightmare.3").isdevelop()
assert not ver("mydevelopmentnightmare.3").isdevelop() assert not ver("=mydevelopmentnightmare.3").isdevelop()
def test_three_segments(): def test_three_segments():
assert_ver_eq("2.0.1", "2.0.1") assert_ver_eq("=2.0.1", "=2.0.1")
assert_ver_lt("2.0", "2.0.1") assert_ver_lt("=2.0", "=2.0.1")
assert_ver_gt("2.0.1", "2.0") assert_ver_gt("=2.0.1", "=2.0")
def test_alpha(): def test_alpha():
# TODO: not sure whether I like this. 2.0.1a is *usually* # TODO: not sure whether I like this. 2.0.1a is *usually*
# TODO: less than 2.0.1, but special-casing it makes version # TODO: less than 2.0.1, but special-casing it makes version
# TODO: comparison complicated. See version.py # TODO: comparison complicated. See version.py
assert_ver_eq("2.0.1a", "2.0.1a") assert_ver_eq("=2.0.1a", "=2.0.1a")
assert_ver_gt("2.0.1a", "2.0.1") assert_ver_gt("=2.0.1a", "=2.0.1")
assert_ver_lt("2.0.1", "2.0.1a") assert_ver_lt("=2.0.1", "=2.0.1a")
def test_patch(): def test_patch():
assert_ver_eq("5.5p1", "5.5p1") assert_ver_eq("=5.5p1", "=5.5p1")
assert_ver_lt("5.5p1", "5.5p2") assert_ver_lt("=5.5p1", "=5.5p2")
assert_ver_gt("5.5p2", "5.5p1") assert_ver_gt("=5.5p2", "=5.5p1")
assert_ver_eq("5.5p10", "5.5p10") assert_ver_eq("=5.5p10", "=5.5p10")
assert_ver_lt("5.5p1", "5.5p10") assert_ver_lt("=5.5p1", "=5.5p10")
assert_ver_gt("5.5p10", "5.5p1") assert_ver_gt("=5.5p10", "=5.5p1")
def test_num_alpha_with_no_separator(): def test_num_alpha_with_no_separator():
assert_ver_lt("10xyz", "10.1xyz") assert_ver_lt("=10xyz", "=10.1xyz")
assert_ver_gt("10.1xyz", "10xyz") assert_ver_gt("=10.1xyz", "=10xyz")
assert_ver_eq("xyz10", "xyz10") assert_ver_eq("=xyz10", "=xyz10")
assert_ver_lt("xyz10", "xyz10.1") assert_ver_lt("=xyz10", "=xyz10.1")
assert_ver_gt("xyz10.1", "xyz10") assert_ver_gt("=xyz10.1", "=xyz10")
def test_alpha_with_dots(): def test_alpha_with_dots():
assert_ver_eq("xyz.4", "xyz.4") assert_ver_eq("=xyz.4", "=xyz.4")
assert_ver_lt("xyz.4", "8") assert_ver_lt("=xyz.4", "=8")
assert_ver_gt("8", "xyz.4") assert_ver_gt("=8", "=xyz.4")
assert_ver_lt("xyz.4", "2") assert_ver_lt("=xyz.4", "=2")
assert_ver_gt("2", "xyz.4") assert_ver_gt("=2", "=xyz.4")
def test_nums_and_patch(): def test_nums_and_patch():
assert_ver_lt("5.5p2", "5.6p1") assert_ver_lt("=5.5p2", "=5.6p1")
assert_ver_gt("5.6p1", "5.5p2") assert_ver_gt("=5.6p1", "=5.5p2")
assert_ver_lt("5.6p1", "6.5p1") assert_ver_lt("=5.6p1", "=6.5p1")
assert_ver_gt("6.5p1", "5.6p1") assert_ver_gt("=6.5p1", "=5.6p1")
def test_rc_versions(): def test_rc_versions():
assert_ver_gt("6.0.rc1", "6.0") assert_ver_gt("=6.0.rc1", "=6.0")
assert_ver_lt("6.0", "6.0.rc1") assert_ver_lt("=6.0", "=6.0.rc1")
def test_alpha_beta(): def test_alpha_beta():
assert_ver_gt("10b2", "10a1") assert_ver_gt("=10b2", "=10a1")
assert_ver_lt("10a2", "10b2") assert_ver_lt("=10a2", "=10b2")
def test_double_alpha(): def test_double_alpha():
assert_ver_eq("1.0aa", "1.0aa") assert_ver_eq("=1.0aa", "=1.0aa")
assert_ver_lt("1.0a", "1.0aa") assert_ver_lt("=1.0a", "=1.0aa")
assert_ver_gt("1.0aa", "1.0a") assert_ver_gt("=1.0aa", "=1.0a")
def test_padded_numbers(): def test_padded_numbers():
assert_ver_eq("10.0001", "10.0001") assert_ver_eq("=10.0001", "=10.0001")
assert_ver_eq("10.0001", "10.1") assert_ver_eq("=10.0001", "=10.1")
assert_ver_eq("10.1", "10.0001") assert_ver_eq("=10.1", "=10.0001")
assert_ver_lt("10.0001", "10.0039") assert_ver_lt("=10.0001", "=10.0039")
assert_ver_gt("10.0039", "10.0001") assert_ver_gt("=10.0039", "=10.0001")
def test_close_numbers(): def test_close_numbers():
assert_ver_lt("4.999.9", "5.0") assert_ver_lt("=4.999.9", "=5.0")
assert_ver_gt("5.0", "4.999.9") assert_ver_gt("=5.0", "=4.999.9")
def test_date_stamps(): def test_date_stamps():
assert_ver_eq("20101121", "20101121") assert_ver_eq("=20101121", "=20101121")
assert_ver_lt("20101121", "20101122") assert_ver_lt("=20101121", "=20101122")
assert_ver_gt("20101122", "20101121") assert_ver_gt("=20101122", "=20101121")
def test_underscores(): def test_underscores():
assert_ver_eq("2_0", "2_0") assert_ver_eq("=2_0", "=2_0")
assert_ver_eq("2.0", "2_0") assert_ver_eq("=2.0", "=2_0")
assert_ver_eq("2_0", "2.0") assert_ver_eq("=2_0", "=2.0")
assert_ver_eq("2-0", "2_0") assert_ver_eq("=2-0", "=2_0")
assert_ver_eq("2_0", "2-0") assert_ver_eq("=2_0", "=2-0")
def test_rpm_oddities(): def test_rpm_oddities():
assert_ver_eq("1b.fc17", "1b.fc17") assert_ver_eq("=1b.fc17", "=1b.fc17")
assert_ver_lt("1b.fc17", "1.fc17") assert_ver_lt("=1b.fc17", "=1.fc17")
assert_ver_gt("1.fc17", "1b.fc17") assert_ver_gt("=1.fc17", "=1b.fc17")
assert_ver_eq("1g.fc17", "1g.fc17") assert_ver_eq("=1g.fc17", "=1g.fc17")
assert_ver_gt("1g.fc17", "1.fc17") assert_ver_gt("=1g.fc17", "=1.fc17")
assert_ver_lt("1.fc17", "1g.fc17") assert_ver_lt("=1.fc17", "=1g.fc17")
# Stuff below here is not taken from RPM's tests and is # Stuff below here is not taken from RPM's tests and is
@ -267,24 +276,24 @@ def test_version_ranges():
def test_contains(): def test_contains():
assert_in("1.3", "1.2:1.4") assert_in("=1.3", "1.2:1.4")
assert_in("1.2.5", "1.2:1.4") assert_in("=1.2.5", "1.2:1.4")
assert_in("1.3.5", "1.2:1.4") assert_in("=1.3.5", "1.2:1.4")
assert_in("1.3.5-7", "1.2:1.4") assert_in("=1.3.5-7", "1.2:1.4")
assert_not_in("1.1", "1.2:1.4") assert_not_in("=1.1", "1.2:1.4")
assert_not_in("1.5", "1.2:1.4") assert_not_in("=1.5", "1.2:1.4")
assert_not_in("1.5", "1.5.1:1.6") assert_not_in("=1.5", "1.5.1:1.6")
assert_not_in("1.5", "1.5.1:") assert_not_in("=1.5", "1.5.1:")
assert_in("1.4.2", "1.2:1.4") assert_in("=1.4.2", "1.2:1.4")
assert_not_in("1.4.2", "1.2:1.4.0") assert_not_in("=1.4.2", "1.2:1.4.0")
assert_in("1.2.8", "1.2.7:1.4") assert_in("=1.2.8", "1.2.7:1.4")
assert_in("1.2.7:1.4", ":") assert_in("1.2.7:1.4", ":")
assert_not_in("1.2.5", "1.2.7:1.4") assert_not_in("=1.2.5", "1.2.7:1.4")
assert_in("1.4.1", "1.2.7:1.4") assert_in("=1.4.1", "1.2.7:1.4")
assert_not_in("1.4.1", "1.2.7:1.4.0") assert_not_in("=1.4.1", "1.2.7:1.4.0")
def test_in_list(): def test_in_list():
@ -370,6 +379,8 @@ def test_intersection():
check_intersection(["2.5:2.7"], ["1.1:2.7"], ["2.5:3.0", "1.0"]) check_intersection(["2.5:2.7"], ["1.1:2.7"], ["2.5:3.0", "1.0"])
check_intersection(["0:1"], [":"], ["0:1"]) check_intersection(["0:1"], [":"], ["0:1"])
check_intersection(["=ref=1.0", "=1.1"], ["=ref=1.0", "1.1"], ["1:1.0", "=1.1"])
def test_intersect_with_containment(): def test_intersect_with_containment():
check_intersection("1.6.5", "1.6.5", ":1.6") check_intersection("1.6.5", "1.6.5", ":1.6")
@ -397,6 +408,8 @@ def test_union_with_containment():
# Tests successor/predecessor case. # Tests successor/predecessor case.
check_union("1:4", "1:2", "3:4") check_union("1:4", "1:2", "3:4")
check_union(["1:1.0", "1.1"], ["=ref=1.0", "1.1"], ["1:1.0", "=1.1"])
def test_basic_version_satisfaction(): def test_basic_version_satisfaction():
assert_satisfies("4.7.3", "4.7.3") assert_satisfies("4.7.3", "4.7.3")
@ -522,7 +535,7 @@ def test_up_to():
def test_repr_and_str(): def test_repr_and_str():
def check_repr_and_str(vrs): def check_repr_and_str(vrs):
a = Version(vrs) a = Version(vrs)
assert repr(a) == "VersionBase('" + vrs + "')" assert repr(a) == f'Version("{vrs}")'
b = eval(repr(a)) b = eval(repr(a))
assert a == b assert a == b
assert str(a) == vrs assert str(a) == vrs
@ -546,19 +559,19 @@ def test_get_item():
assert isinstance(a[1], int) assert isinstance(a[1], int)
# Test slicing # Test slicing
b = a[0:2] b = a[0:2]
assert isinstance(b, VersionBase) assert isinstance(b, StandardVersion)
assert b == Version("0.1") assert b == Version("0.1")
assert repr(b) == "VersionBase('0.1')" assert repr(b) == 'Version("0.1")'
assert str(b) == "0.1" assert str(b) == "0.1"
b = a[0:3] b = a[0:3]
assert isinstance(b, VersionBase) assert isinstance(b, StandardVersion)
assert b == Version("0.1_2") assert b == Version("0.1_2")
assert repr(b) == "VersionBase('0.1_2')" assert repr(b) == 'Version("0.1_2")'
assert str(b) == "0.1_2" assert str(b) == "0.1_2"
b = a[1:] b = a[1:]
assert isinstance(b, VersionBase) assert isinstance(b, StandardVersion)
assert b == Version("1_2-3") assert b == Version("1_2-3")
assert repr(b) == "VersionBase('1_2-3')" assert repr(b) == 'Version("1_2-3")'
assert str(b) == "1_2-3" assert str(b) == "1_2-3"
# Raise TypeError on tuples # Raise TypeError on tuples
with pytest.raises(TypeError): with pytest.raises(TypeError):
@ -566,12 +579,12 @@ def test_get_item():
def test_list_highest(): def test_list_highest():
vl = VersionList(["master", "1.2.3", "develop", "3.4.5", "foobar"]) vl = VersionList(["=master", "=1.2.3", "=develop", "=3.4.5", "=foobar"])
assert vl.highest() == Version("develop") assert vl.highest() == Version("develop")
assert vl.lowest() == Version("foobar") assert vl.lowest() == Version("foobar")
assert vl.highest_numeric() == Version("3.4.5") assert vl.highest_numeric() == Version("3.4.5")
vl2 = VersionList(["master", "develop"]) vl2 = VersionList(["=master", "=develop"])
assert vl2.highest_numeric() is None assert vl2.highest_numeric() is None
assert vl2.preferred() == Version("develop") assert vl2.preferred() == Version("develop")
assert vl2.lowest() == Version("master") assert vl2.lowest() == Version("master")
@ -593,10 +606,8 @@ def test_versions_from_git(git, mock_git_version_info, monkeypatch, mock_package
for commit in commits: for commit in commits:
spec = spack.spec.Spec("git-test-commit@%s" % commit) spec = spack.spec.Spec("git-test-commit@%s" % commit)
version = spec.version version: GitVersion = spec.version
comparator = [ comparator = [str(v) if not isinstance(v, int) else v for v in version.ref_version]
str(v) if not isinstance(v, int) else v for v in version._cmp(version.ref_lookup)
]
with working_dir(repo_path): with working_dir(repo_path):
git("checkout", commit) git("checkout", commit)
@ -655,14 +666,14 @@ def test_git_ref_comparisons(mock_git_version_info, install_mockery, mock_packag
spec_tag.concretize() spec_tag.concretize()
assert spec_tag.satisfies("@1.0") assert spec_tag.satisfies("@1.0")
assert not spec_tag.satisfies("@1.1:") assert not spec_tag.satisfies("@1.1:")
assert str(spec_tag.version) == "git.v1.0" assert str(spec_tag.version) == "git.v1.0=1.0"
# Spec based on branch 1.x # Spec based on branch 1.x
spec_branch = spack.spec.Spec("git-test-commit@git.1.x") spec_branch = spack.spec.Spec("git-test-commit@git.1.x")
spec_branch.concretize() spec_branch.concretize()
assert spec_branch.satisfies("@1.2") assert spec_branch.satisfies("@1.2")
assert spec_branch.satisfies("@1.1:1.3") assert spec_branch.satisfies("@1.1:1.3")
assert str(spec_branch.version) == "git.1.x" assert str(spec_branch.version) == "git.1.x=1.2"
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -676,6 +687,7 @@ def test_git_ref_comparisons(mock_git_version_info, install_mockery, mock_packag
], ],
) )
def test_version_git_vs_base(string, git): def test_version_git_vs_base(string, git):
assert is_git_version(string) == git
assert isinstance(Version(string), GitVersion) == git assert isinstance(Version(string), GitVersion) == git
@ -713,21 +725,9 @@ def test_version_range_satisfies_means_nonempty_intersection():
assert not y.satisfies(x) assert not y.satisfies(x)
@pytest.mark.regression("26482")
def test_version_list_with_range_included_in_concrete_version_interpreted_as_range():
# Note: this test only tests whether we can construct a version list of a range
# and a version, where the range is contained in the version when it is interpreted
# as a range. That is: Version('3.1') interpreted as VersionRange('3.1', '3.1').
# Cleary it *shouldn't* be interpreted that way, but that is how Spack currently
# behaves, and this test only ensures that creating a VersionList of this type
# does not throw like reported in the linked Github issue.
VersionList([Version("3.1"), VersionRange("3.1.1", "3.1.2")])
@pytest.mark.xfail
def test_version_list_with_range_and_concrete_version_is_not_concrete(): def test_version_list_with_range_and_concrete_version_is_not_concrete():
v = VersionList([Version("3.1"), VersionRange("3.1.1", "3.1.2")]) v = VersionList([Version("3.1"), VersionRange(Version("3.1.1"), Version("3.1.2"))])
assert v.concrete assert not v.concrete
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -744,15 +744,14 @@ def test_git_ref_can_be_assigned_a_version(vstring, eq_vstring, is_commit):
v = Version(vstring) v = Version(vstring)
v_equivalent = Version(eq_vstring) v_equivalent = Version(eq_vstring)
assert v.is_commit == is_commit assert v.is_commit == is_commit
assert v.is_ref
assert not v._ref_lookup assert not v._ref_lookup
assert v_equivalent.version == v.ref_version assert v_equivalent == v.ref_version
@pytest.mark.parametrize( @pytest.mark.parametrize(
"lhs_str,rhs_str,expected", "lhs_str,rhs_str,expected",
[ [
# VersionBase # StandardVersion
("4.7.3", "4.7.3", (True, True, True)), ("4.7.3", "4.7.3", (True, True, True)),
("4.7.3", "4.7", (True, True, False)), ("4.7.3", "4.7", (True, True, False)),
("4.7.3", "4", (True, True, False)), ("4.7.3", "4", (True, True, False)),
@ -808,3 +807,170 @@ def test_git_versions_without_explicit_reference(
for test_str, expected in tested_intersects: for test_str, expected in tested_intersects:
assert spec.intersects(test_str) is expected, test_str assert spec.intersects(test_str) is expected, test_str
def test_total_order_versions_and_ranges():
# The set of version ranges and individual versions are comparable, which is used in
# VersionList. The comparsion across types is based on default version comparsion
# of StandardVersion, GitVersion.ref_version, and ClosedOpenRange.lo.
# StandardVersion / GitVersion (at equal ref version)
assert_ver_lt("=1.2", "git.ref=1.2")
assert_ver_gt("git.ref=1.2", "=1.2")
# StandardVersion / GitVersion (at different ref versions)
assert_ver_lt("git.ref=1.2", "=1.3")
assert_ver_gt("=1.3", "git.ref=1.2")
assert_ver_lt("=1.2", "git.ref=1.3")
assert_ver_gt("git.ref=1.3", "=1.2")
# GitVersion / ClosedOpenRange (at equal ref/lo version)
assert_ver_lt("git.ref=1.2", "1.2")
assert_ver_gt("1.2", "git.ref=1.2")
# GitVersion / ClosedOpenRange (at different ref/lo version)
assert_ver_lt("git.ref=1.2", "1.3")
assert_ver_gt("1.3", "git.ref=1.2")
assert_ver_lt("1.2", "git.ref=1.3")
assert_ver_gt("git.ref=1.3", "1.2")
# StandardVersion / ClosedOpenRange (at equal lo version)
assert_ver_lt("=1.2", "1.2")
assert_ver_gt("1.2", "=1.2")
# StandardVersion / ClosedOpenRange (at different lo version)
assert_ver_lt("=1.2", "1.3")
assert_ver_gt("1.3", "=1.2")
assert_ver_lt("1.2", "=1.3")
assert_ver_gt("=1.3", "1.2")
def test_git_version_accessors():
"""Test whether iteration, indexing, slicing, dotted, dashed, and underscored works for
GitVersion."""
v = GitVersion("my_branch=1.2-3")
assert [x for x in v] == [1, 2, 3]
assert v[0] == 1
assert v[1] == 2
assert v[2] == 3
assert v[0:2] == Version("1.2")
assert v[0:10] == Version("1.2.3")
assert str(v.dotted) == "1.2.3"
assert str(v.dashed) == "1-2-3"
assert str(v.underscored) == "1_2_3"
assert v.up_to(1) == Version("1")
assert v.up_to(2) == Version("1.2")
assert len(v) == 3
assert not v.isdevelop()
assert GitVersion("my_branch=develop").isdevelop()
def test_boolness_of_versions():
# We do implement __len__, but at the end of the day versions are used as elements in
# the first place, not as lists of version components. So VersionList(...).concrete
# should be truthy even when there are no version components.
assert bool(Version("1.2"))
assert bool(Version("1.2").up_to(0))
# bool(GitVersion) shouldn't trigger a ref lookup.
assert bool(GitVersion("a" * 40))
def test_version_list_normalization():
# Git versions and ordinary versions can live together in a VersionList
assert len(VersionList(["=1.2", "ref=1.2"])) == 2
# But when a range is added, the only disjoint bit is the range.
assert VersionList(["=1.2", "ref=1.2", "ref=1.3", "1.2:1.3"]) == VersionList(["1.2:1.3"])
# Also test normalization when using ver.
assert ver("=1.0,ref=1.0,1.0:2.0") == ver(["1.0:2.0"])
assert ver("=1.0,1.0:2.0,ref=1.0") == ver(["1.0:2.0"])
assert ver("1.0:2.0,=1.0,ref=1.0") == ver(["1.0:2.0"])
@pytest.mark.parametrize("version", ["=1.2", "git.ref=1.2", "1.2"])
def test_version_comparison_with_list_fails(version):
vlist = VersionList(["=1.3"])
with pytest.raises(TypeError):
version < vlist
with pytest.raises(TypeError):
vlist < version
with pytest.raises(TypeError):
version <= vlist
with pytest.raises(TypeError):
vlist <= version
with pytest.raises(TypeError):
version >= vlist
with pytest.raises(TypeError):
vlist >= version
with pytest.raises(TypeError):
version > vlist
with pytest.raises(TypeError):
vlist > version
def test_inclusion_upperbound():
is_specific = spack.spec.Spec("x@=1.2")
is_range = spack.spec.Spec("x@1.2")
upperbound = spack.spec.Spec("x@:1.2.0")
# The exact version is included in the range
assert is_specific.satisfies(upperbound)
# But the range 1.2:1.2 is not, since it includes for example 1.2.1
assert not is_range.satisfies(upperbound)
# They do intersect of course.
assert is_specific.intersects(upperbound) and is_range.intersects(upperbound)
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_git_version_repo_attached_after_serialization(
mock_git_version_info, mock_packages, monkeypatch
):
"""Test that a GitVersion instance can be serialized and deserialized
without losing its repository reference.
"""
repo_path, _, commits = mock_git_version_info
monkeypatch.setattr(
spack.package_base.PackageBase, "git", "file://%s" % repo_path, raising=False
)
spec = spack.spec.Spec(f"git-test-commit@{commits[-2]}").concretized()
# Before serialization, the repo is attached
assert spec.satisfies("@1.0")
# After serialization, the repo is still attached
assert spack.spec.Spec.from_dict(spec.to_dict()).satisfies("@1.0")
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_resolved_git_version_is_shown_in_str(mock_git_version_info, mock_packages, monkeypatch):
"""Test that a GitVersion from a commit without a user supplied version is printed
as <hash>=<version>, and not just <hash>."""
repo_path, _, commits = mock_git_version_info
monkeypatch.setattr(
spack.package_base.PackageBase, "git", "file://%s" % repo_path, raising=False
)
commit = commits[-3]
spec = spack.spec.Spec(f"git-test-commit@{commit}").concretized()
assert spec.version.satisfies(ver("1.0"))
assert str(spec.version) == f"{commit}=1.0-git.1"
def test_unresolvable_git_versions_error(mock_packages):
"""Test that VersionLookupError is raised when a git prop is not set on a package."""
with pytest.raises(VersionLookupError):
# The package exists, but does not have a git property set. When dereferencing
# the version, we should get VersionLookupError, not a generic AttributeError.
spack.spec.Spec(f"git-test-commit@{'a' * 40}").version.ref_version

View file

@ -16,7 +16,7 @@
import spack.util.s3 import spack.util.s3
import spack.util.url as url_util import spack.util.url as url_util
import spack.util.web import spack.util.web
from spack.version import ver from spack.version import Version
def _create_url(relative_url): def _create_url(relative_url):
@ -102,47 +102,47 @@ def test_spider_no_response(monkeypatch):
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_find_versions_of_archive_0(): def test_find_versions_of_archive_0():
versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=0) versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=0)
assert ver("0.0.0") in versions assert Version("0.0.0") in versions
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_find_versions_of_archive_1(): def test_find_versions_of_archive_1():
versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=1) versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=1)
assert ver("0.0.0") in versions assert Version("0.0.0") in versions
assert ver("1.0.0") in versions assert Version("1.0.0") in versions
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_find_versions_of_archive_2(): def test_find_versions_of_archive_2():
versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=2) versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=2)
assert ver("0.0.0") in versions assert Version("0.0.0") in versions
assert ver("1.0.0") in versions assert Version("1.0.0") in versions
assert ver("2.0.0") in versions assert Version("2.0.0") in versions
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_find_exotic_versions_of_archive_2(): def test_find_exotic_versions_of_archive_2():
versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=2) versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=2)
# up for grabs to make this better. # up for grabs to make this better.
assert ver("2.0.0b2") in versions assert Version("2.0.0b2") in versions
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_find_versions_of_archive_3(): def test_find_versions_of_archive_3():
versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=3) versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=3)
assert ver("0.0.0") in versions assert Version("0.0.0") in versions
assert ver("1.0.0") in versions assert Version("1.0.0") in versions
assert ver("2.0.0") in versions assert Version("2.0.0") in versions
assert ver("3.0") in versions assert Version("3.0") in versions
assert ver("4.5") in versions assert Version("4.5") in versions
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_find_exotic_versions_of_archive_3(): def test_find_exotic_versions_of_archive_3():
versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=3) versions = spack.util.web.find_versions_of_archive(root_tarball, root, list_depth=3)
assert ver("2.0.0b2") in versions assert Version("2.0.0b2") in versions
assert ver("3.0a1") in versions assert Version("3.0a1") in versions
assert ver("4.5-rc5") in versions assert Version("4.5-rc5") in versions
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
@ -150,7 +150,7 @@ def test_find_versions_of_archive_with_fragment():
versions = spack.util.web.find_versions_of_archive( versions = spack.util.web.find_versions_of_archive(
root_tarball, root_with_fragment, list_depth=0 root_tarball, root_with_fragment, list_depth=0
) )
assert ver("5.0.0") in versions assert Version("5.0.0") in versions
def test_get_header(): def test_get_header():

File diff suppressed because it is too large Load diff

View file

@ -10,9 +10,9 @@ class Callpath(Package):
homepage = "https://github.com/tgamblin/callpath" homepage = "https://github.com/tgamblin/callpath"
url = "http://github.com/tgamblin/callpath-1.0.tar.gz" url = "http://github.com/tgamblin/callpath-1.0.tar.gz"
version(0.8, md5="0123456789abcdef0123456789abcdef") version("0.8", md5="0123456789abcdef0123456789abcdef")
version(0.9, md5="0123456789abcdef0123456789abcdef") version("0.9", md5="0123456789abcdef0123456789abcdef")
version(1.0, md5="0123456789abcdef0123456789abcdef") version("1.0", md5="0123456789abcdef0123456789abcdef")
depends_on("dyninst") depends_on("dyninst")
depends_on("mpi") depends_on("mpi")

View file

@ -10,9 +10,9 @@ class ConflictParent(Package):
homepage = "https://github.com/tgamblin/callpath" homepage = "https://github.com/tgamblin/callpath"
url = "http://github.com/tgamblin/callpath-1.0.tar.gz" url = "http://github.com/tgamblin/callpath-1.0.tar.gz"
version(0.8, md5="0123456789abcdef0123456789abcdef") version("0.8", md5="0123456789abcdef0123456789abcdef")
version(0.9, md5="0123456789abcdef0123456789abcdef") version("0.9", md5="0123456789abcdef0123456789abcdef")
version(1.0, md5="0123456789abcdef0123456789abcdef") version("1.0", md5="0123456789abcdef0123456789abcdef")
depends_on("conflict") depends_on("conflict")

View file

@ -10,9 +10,9 @@ class Conflict(Package):
homepage = "https://github.com/tgamblin/callpath" homepage = "https://github.com/tgamblin/callpath"
url = "http://github.com/tgamblin/callpath-1.0.tar.gz" url = "http://github.com/tgamblin/callpath-1.0.tar.gz"
version(0.8, md5="0123456789abcdef0123456789abcdef") version("0.8", md5="0123456789abcdef0123456789abcdef")
version(0.9, md5="0123456789abcdef0123456789abcdef") version("0.9", md5="0123456789abcdef0123456789abcdef")
version(1.0, md5="0123456789abcdef0123456789abcdef") version("1.0", md5="0123456789abcdef0123456789abcdef")
variant("foo", default=True, description="") variant("foo", default=True, description="")

View file

@ -14,8 +14,8 @@ class Fftw(Package):
homepage = "http://www.example.com" homepage = "http://www.example.com"
url = "http://www.example.com/fftw-1.0.tar.gz" url = "http://www.example.com/fftw-1.0.tar.gz"
version(2.0, md5="abcdef1234567890abcdef1234567890") version("2.0", md5="abcdef1234567890abcdef1234567890")
version(1.0, md5="1234567890abcdef1234567890abcdef") version("1.0", md5="1234567890abcdef1234567890abcdef")
variant("mpi", default=False, description="Enable MPI") variant("mpi", default=False, description="Enable MPI")

View file

@ -11,7 +11,7 @@ class Hdf5(Package):
homepage = "http://www.llnl.gov" homepage = "http://www.llnl.gov"
url = "http://www.llnl.gov/hdf5-1.0.tar.gz" url = "http://www.llnl.gov/hdf5-1.0.tar.gz"
version(2.3, md5="0123456789abcdef0123456789abcdef") version("2.3", md5="0123456789abcdef0123456789abcdef")
variant("mpi", default=True, description="Enable mpi") variant("mpi", default=True, description="Enable mpi")

View file

@ -13,7 +13,7 @@ class ImpossibleConcretization(Package):
homepage = "http://www.example.com" homepage = "http://www.example.com"
url = "http://www.example.com/example-1.0.tar.gz" url = "http://www.example.com/example-1.0.tar.gz"
version(1.0, md5="0123456789abcdef0123456789abcdef") version("1.0", md5="0123456789abcdef0123456789abcdef")
conflicts("target=x86_64:") conflicts("target=x86_64:")
conflicts("target=aarch64:") conflicts("target=aarch64:")

View file

@ -14,7 +14,7 @@ class IndirectMpich(Package):
homepage = "http://www.example.com" homepage = "http://www.example.com"
url = "http://www.example.com/indirect_mpich-1.0.tar.gz" url = "http://www.example.com/indirect_mpich-1.0.tar.gz"
version(1.0, md5="0123456789abcdef0123456789abcdef") version("1.0", md5="0123456789abcdef0123456789abcdef")
depends_on("mpi") depends_on("mpi")
depends_on("direct-mpich") depends_on("direct-mpich")

View file

@ -14,10 +14,10 @@ class Libdwarf(Package):
url = "http://www.prevanders.net/libdwarf-20130729.tar.gz" url = "http://www.prevanders.net/libdwarf-20130729.tar.gz"
list_url = homepage list_url = homepage
version(20130729, md5="64b42692e947d5180e162e46c689dfbf") version("20130729", md5="64b42692e947d5180e162e46c689dfbf")
version(20130207, md5="0123456789abcdef0123456789abcdef") version("20130207", md5="0123456789abcdef0123456789abcdef")
version(20111030, md5="0123456789abcdef0123456789abcdef") version("20111030", md5="0123456789abcdef0123456789abcdef")
version(20070703, md5="0123456789abcdef0123456789abcdef") version("20070703", md5="0123456789abcdef0123456789abcdef")
depends_on("libelf") depends_on("libelf")

View file

@ -10,7 +10,7 @@ class ModulePathSeparator(Package):
homepage = "http://www.llnl.gov" homepage = "http://www.llnl.gov"
url = "http://www.llnl.gov/module-path-separator-1.0.tar.gz" url = "http://www.llnl.gov/module-path-separator-1.0.tar.gz"
version(1.0, md5="0123456789abcdef0123456789abcdef") version("1.0", md5="0123456789abcdef0123456789abcdef")
def setup_run_environment(self, env): def setup_run_environment(self, env):
env.append_path("COLON", "foo") env.append_path("COLON", "foo")

View file

@ -10,10 +10,10 @@ class MultivalueVariant(Package):
homepage = "http://www.llnl.gov" homepage = "http://www.llnl.gov"
url = "http://www.llnl.gov/mpileaks-1.0.tar.gz" url = "http://www.llnl.gov/mpileaks-1.0.tar.gz"
version(1.0, md5="0123456789abcdef0123456789abcdef") version("1.0", md5="0123456789abcdef0123456789abcdef")
version(2.1, md5="0123456789abcdef0123456789abcdef") version("2.1", md5="0123456789abcdef0123456789abcdef")
version(2.2, md5="0123456789abcdef0123456789abcdef") version("2.2", md5="0123456789abcdef0123456789abcdef")
version(2.3, md5="0123456789abcdef0123456789abcdef") version("2.3", md5="0123456789abcdef0123456789abcdef")
variant("debug", default=False, description="Debug variant") variant("debug", default=False, description="Debug variant")
variant( variant(

View file

@ -14,7 +14,7 @@ class QuantumEspresso(Package):
homepage = "http://www.example.com" homepage = "http://www.example.com"
url = "http://www.example.com/qe-1.0.tar.gz" url = "http://www.example.com/qe-1.0.tar.gz"
version(1.0, md5="1234567890abcdef1234567890abcdef") version("1.0", md5="1234567890abcdef1234567890abcdef")
variant("invino", default=True, description="?") variant("invino", default=True, description="?")
variant("veritas", default=True, description="?") variant("veritas", default=True, description="?")

View file

@ -11,7 +11,7 @@ class SinglevalueVariant(Package):
homepage = "http://www.llnl.gov" homepage = "http://www.llnl.gov"
url = "http://www.llnl.gov/mpileaks-1.0.tar.gz" url = "http://www.llnl.gov/mpileaks-1.0.tar.gz"
version(1.0, md5="0123456789abcdef0123456789abcdef") version("1.0", md5="0123456789abcdef0123456789abcdef")
variant( variant(
"fum", "fum",

View file

@ -81,7 +81,7 @@ class Genie(Package):
def url_for_version(self, version): def url_for_version(self, version):
url = "https://github.com/GENIE-MC/Generator/archive/R-{0}.tar.gz" url = "https://github.com/GENIE-MC/Generator/archive/R-{0}.tar.gz"
if version >= Version(3): if version >= Version("3"):
return url.format("{0}_{1:02d}_{2:02d}".format(*version)) return url.format("{0}_{1:02d}_{2:02d}".format(*version))
else: else:
return url.format(version.underscored) return url.format(version.underscored)

View file

@ -37,7 +37,7 @@ def install(self, spec, prefix):
template_name = "{0.architecture}-{0.compiler.name}" template_name = "{0.architecture}-{0.compiler.name}"
grackle_architecture = template_name.format(spec) grackle_architecture = template_name.format(spec)
link_variables = ( link_variables = (
"MACH_AR = ar" if spec.version < Version(2.2) else "MACH_LIBTOOL = libtool" "MACH_AR = ar" if spec.version < Version("2.2") else "MACH_LIBTOOL = libtool"
) )
substitutions = { substitutions = {
"@ARCHITECTURE": grackle_architecture, "@ARCHITECTURE": grackle_architecture,

View file

@ -54,7 +54,7 @@ class Libyogrt(AutotoolsPackage):
variant("static", default="False", description="build static library") variant("static", default="False", description="build static library")
def url_for_version(self, version): def url_for_version(self, version):
if version < Version(1.21): if version < Version("1.21"):
return "https://github.com/LLNL/libyogrt/archive/%s.tar.gz" % version return "https://github.com/LLNL/libyogrt/archive/%s.tar.gz" % version
else: else:
return "https://github.com/LLNL/libyogrt/releases/download/{0}/libyogrt-{0}.tar.gz".format( return "https://github.com/LLNL/libyogrt/releases/download/{0}/libyogrt-{0}.tar.gz".format(

View file

@ -20,7 +20,7 @@ class PyApacheBeam(PythonPackage):
depends_on("py-cython@0.28.1:", type="build") depends_on("py-cython@0.28.1:", type="build")
depends_on("py-avro-python3@1.8.1:1.9.1,1.9.3:1.9", type=("build", "run")) depends_on("py-avro-python3@1.8.1:1.9.1,1.9.3:1.9", type=("build", "run"))
depends_on("py-crcmod@1.7:1", type=("build", "run")) depends_on("py-crcmod@1.7:1", type=("build", "run"))
depends_on("py-dill@0.3.1.1:0.3.1", type=("build", "run")) depends_on("py-dill@0.3.1:0.3.1", type=("build", "run"))
depends_on("py-fastavro@0.21.4:0.23", type=("build", "run")) depends_on("py-fastavro@0.21.4:0.23", type=("build", "run"))
depends_on("py-future@0.18.2:0", type=("build", "run")) depends_on("py-future@0.18.2:0", type=("build", "run"))
depends_on("py-grpcio@1.29:1", type=("build", "run")) depends_on("py-grpcio@1.29:1", type=("build", "run"))

View file

@ -36,7 +36,7 @@ class PyCutadapt(PythonPackage):
depends_on("py-xopen@0.8.1:0.8", type=("build", "run"), when="@2.5") depends_on("py-xopen@0.8.1:0.8", type=("build", "run"), when="@2.5")
depends_on("py-xopen@0.8.4:0.8", type=("build", "run"), when="@2.6:2.10") depends_on("py-xopen@0.8.4:0.8", type=("build", "run"), when="@2.6:2.10")
depends_on("py-dnaio@0.3:", type=("build", "run"), when="@2.0:2.4") depends_on("py-dnaio@0.3:", type=("build", "run"), when="@2.0:2.4")
depends_on("py-dnaio@0.3.0:0.3", type=("build", "run"), when="@2.5") depends_on("py-dnaio@0.3", type=("build", "run"), when="@2.5")
depends_on("py-dnaio@0.4.0:0.4", type=("build", "run"), when="@2.6") depends_on("py-dnaio@0.4.0:0.4", type=("build", "run"), when="@2.6")
depends_on("py-dnaio@0.4.1:0.4", type=("build", "run"), when="@2.7:2.9") depends_on("py-dnaio@0.4.1:0.4", type=("build", "run"), when="@2.7:2.9")
depends_on("py-dnaio@0.4.2:0.4", type=("build", "run"), when="@2.10") depends_on("py-dnaio@0.4.2:0.4", type=("build", "run"), when="@2.10")

View file

@ -21,4 +21,4 @@ class PySphinxTabs(PythonPackage):
depends_on("py-setuptools", type="build") depends_on("py-setuptools", type="build")
depends_on("py-sphinx@2:4", type=("build", "run")) depends_on("py-sphinx@2:4", type=("build", "run"))
depends_on("py-pygments", type=("build", "run")) depends_on("py-pygments", type=("build", "run"))
depends_on("py-docutils@0.16.0:0.16", type=("build", "run")) depends_on("py-docutils@0.16", type=("build", "run"))

View file

@ -33,6 +33,6 @@ class Qca(CMakePackage):
def cmake_args(self): def cmake_args(self):
args = [] args = []
args.append("-DCMAKE_CXX_STANDARD=11") args.append("-DCMAKE_CXX_STANDARD=11")
if self.spec["qt"].version.up_to(1) == Version(4): if self.spec["qt"].version.up_to(1) == Version("4"):
args.append("-DQT4_BUILD=ON") args.append("-DQT4_BUILD=ON")
return args return args

View file

@ -19,6 +19,6 @@ class Qjson(CMakePackage):
def cmake_args(self): def cmake_args(self):
args = [] args = []
if self.spec["qt"].version.up_to(1) == Version(4): if self.spec["qt"].version.up_to(1) == Version("4"):
args.append("-DQT4_BUILD=ON") args.append("-DQT4_BUILD=ON")
return args return args

View file

@ -19,6 +19,6 @@ class Qtkeychain(CMakePackage):
def cmake_args(self): def cmake_args(self):
args = [] args = []
if self.spec["qt"].version.up_to(1) == Version(4): if self.spec["qt"].version.up_to(1) == Version("4"):
args.append("-DBUILD_WITH_QT4=ON") args.append("-DBUILD_WITH_QT4=ON")
return args return args

View file

@ -68,7 +68,7 @@ def cmake_args(self):
if self.spec.satisfies("+qt"): if self.spec.satisfies("+qt"):
args.append("-DBUILD_wireshark=ON") args.append("-DBUILD_wireshark=ON")
args.append("-DENABLE_APPLICATION_BUNDLE=ON") args.append("-DENABLE_APPLICATION_BUNDLE=ON")
if self.spec["qt"].version >= Version(5): if self.spec["qt"].version >= Version("5"):
args.append("-DENABLE_QT5=ON") args.append("-DENABLE_QT5=ON")
else: else:
args.append("-DENABLE_QT5=OFF") args.append("-DENABLE_QT5=OFF")