Compare commits

...

37 Commits

Author SHA1 Message Date
Massimiliano Culpo
e30fedab10 Update CHANGELOG and version 2024-01-12 10:16:58 +01:00
eugeneswalker
a7c6df1b5a e4s ci: disable gpu test stack (#41296) 2024-01-12 10:16:58 +01:00
Harmen Stoppels
19e86088cd binary_distribution.py: support build cache layout 2 (#41773)
Add forward compatibility for tarballs created by Spack 0.22, which
use build cache layout version 2.

Spack 0.21 continues to produce build cache layout version 1 tarballs.

Build cache layout version 2 also lists parent directories of the
package prefix in the tarball, which is required for certain container
runtimes.
2024-01-11 09:40:22 +01:00
Harmen Stoppels
0295b466d7 Dont expect __qualname__ to exist (#41989) 2024-01-11 09:40:22 +01:00
Harmen Stoppels
68e9547615 installer.py: do not tty.die when cache only fails (#41990) 2024-01-11 09:40:22 +01:00
Harmen Stoppels
0ab9c8b904 installer.py: don't dereference stage before installing from binaries (#41986)
This fixes an issue where pkg.stage throws because a patch cannot be found,
but the patch is redundant because the spec is reused from a build cache and
will be installed from existing binaries.
2024-01-11 09:40:22 +01:00
Miguel Dias Costa
dd768bb6c3 update BerkeleyGW source urls (#38218)
* update url for BerkeleyGW version 3.0.1
* update source urls and add version 3.1.0 to berkeleygw package
2024-01-11 09:40:22 +01:00
Harmen Stoppels
b15f9d011c Spec.format: error on old style format strings (#41934) 2024-01-11 09:40:22 +01:00
Massimiliano Culpo
4f7cce68b8 ASP-based solver: don't error for type mismatch on preferences (#41138)
This commit discards type mismatches or failures to validate a package preference during concretization. The values discarded are logged as debug level messages. It also adds a config audit to help users spot misconfigurations in packages.yaml preferences.
2024-01-11 09:40:22 +01:00
Massimiliano Culpo
f39a1e5fc8 Fix an issue with deconcretization/reconcretization of environments (#41294) 2024-01-11 09:40:22 +01:00
Massimiliano Culpo
df2a3bd531 ASP-based solver: use a unique ID counter (#41290)
* solver: use a unique counter for condition, triggers and effects

* Do not reset counters when re-running setup

  What we need is just a unique ID, it doesn't need
  to start from zero every time.
2024-01-11 09:40:22 +01:00
Todd Gamblin
e29049d9c0 bugfix: sort variants in spack info --variants-by-name (#41389)
This was missed while backporting the new `spack info` command from #40326.

Variants should be sorted by name when invoking `spack info --variants-by-name`.
2024-01-11 09:40:22 +01:00
Stephen Sachs
de2249c334 clingo-bootstrap: use new Spack API for environment modifications (#41574) 2024-01-11 09:40:22 +01:00
Harmen Stoppels
03d7643480 tests: fix more cases of env variables (#41226) 2024-01-11 09:40:22 +01:00
Massimiliano Culpo
9f2b8eef7a Refactor a test to not use the "working_env" fixture (#41308)
Co-authored-by: Harmen Stoppels <me@harmenstoppels.nl>
2024-01-11 09:40:22 +01:00
Harmen Stoppels
f57ac8d2da tests: fix issue with os.environ binding (#41342) 2024-01-11 09:40:22 +01:00
Harmen Stoppels
539fa5c39a test_variant_propagation_with_unify_false: missing fixture (#41345) 2024-01-11 09:40:22 +01:00
Harmen Stoppels
50710c2b6e tests: fix side effects of default_config fixture (#41361)
* tests: default_config drop scope

* use default_config elsewhere

* use parse_install_tree for missing defaults in default config
2024-01-11 09:40:22 +01:00
Harmen Stoppels
f1b2515ce1 tests: add missing mutable db (#41359) 2024-01-11 09:40:22 +01:00
Harmen Stoppels
37f65e769c unit tests: replace /bin/bash with /bin/sh (#41495) 2024-01-11 09:40:22 +01:00
Harmen Stoppels
16b600c193 tests: use temporary_store (#41369) 2024-01-11 09:40:22 +01:00
Harmen Stoppels
bb62f71aa0 asp.py: remove "CLI" reference (#41718)
Can also be an environment root, or programatically
`Spec("x").concretized()`.
2024-01-11 09:40:22 +01:00
Juan Miguel Carceller
fff8e16cdd Add a webgui patch (#41404)
Co-authored-by: jmcarcell <jmcarcell@users.noreply.github.com>
2024-01-11 09:40:22 +01:00
Dave Keeshan
33c287eed8 Fix filter_compiler_wrapper where compiler is None (#41502)
Fix filer_compiler_wrapper for cases where the compiler returned in None, this happens on some installed gcc systems that do not have fortran built into them as standard, e.g. gcc@11.4.0 on ubuntu 22.04
2024-01-11 09:40:22 +01:00
Robert Cohn
2d5ccd3068 handle use of an unconfigured compiler (#41213) 2024-01-10 20:28:14 +01:00
Michael Kuhn
125085c580 Fix multi-word aliases (#41126)
PR #40929 reverted the argument parsing to make `spack --verbose
install` work again. It looks like `--verbose` is the only instance
where this kind of argument inheritance is used since all other commands
override arguments with the same name instead. For instance, `spack
--bootstrap clean` does not invoke `spack clean --bootstrap`.

Therefore, fix multi-line aliases again by parsing the resolved
arguments and instead explicitly pass down `args.verbose` to commands.
2024-01-10 20:28:14 +01:00
Massimiliano Culpo
e70e401be1 spack graph: fix coloring with environments (#41240)
If we use all specs, we won't color correctly build-only dependencies
2024-01-10 20:28:14 +01:00
John W. Parent
e8bbd7763c MSVC preview version breaks clingo build (#41185)
Co-authored-by: Harmen Stoppels <harmenstoppels@gmail.com>
2024-01-10 20:28:14 +01:00
Harmen Stoppels
712db8c85c setup_platform_environment before package env mods (#41205)
This roughly restores the order of operation from Spack 0.20,
where where `AutotoolsPackage.setup_build_environment` would
override the env variable set in `setup_platform_environment` on
macOS.
2024-01-10 20:28:14 +01:00
Massimiliano Culpo
47c560d526 ASP-based solver: don't emit spurious debug output (#41218)
When improving the error message, we started #showing in the
answer set a lot more symbols - but we forgot to suppress the
debug messages warning about UNKNOWN SYMBOLs
2024-01-10 20:28:14 +01:00
Harmen Stoppels
5b386cf9b1 test_which: do not mutate os.environ 2024-01-10 20:28:14 +01:00
Harmen Stoppels
2cbe84b1ee docs: document how spack picks a version / variant (#41070) 2024-01-10 20:28:14 +01:00
Massimiliano Culpo
e1f98fd206 Improve the error message for deprecated preferences (#41075)
Improves the warning for deprecated preferences, and adds a configuration
audit to get files:lines details of the issues.

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-01-10 20:28:14 +01:00
Massimiliano Culpo
d5ef4f8c83 Add audit check to spot when= arguments using wrong named specs (#41107)
* Add audit check to spot when= arguments using named specs

* Fix package issues caught by the new audit
2024-01-10 20:28:14 +01:00
Harmen Stoppels
b49022822d docs: packages config on separate page, demote bootstrapping (#41085) 2024-01-10 20:28:14 +01:00
Massimiliano Culpo
198bd87914 Fix infinite recursion when computing concretization errors (#41061) 2024-01-10 20:28:14 +01:00
Harmen Stoppels
080a781b81 Set version to 0.21.1.dev0 2024-01-10 20:28:14 +01:00
56 changed files with 1330 additions and 894 deletions

View File

@@ -1,3 +1,29 @@
# v0.21.1 (2024-01-11)
## New features
- Add support for reading buildcaches created by Spack v0.22 (#41773)
## Bugfixes
- spack graph: fix coloring with environments (#41240)
- spack info: sort variants in --variants-by-name (#41389)
- Spec.format: error on old style format strings (#41934)
- ASP-based solver:
- fix infinite recursion when computing concretization errors (#41061)
- don't error for type mismatch on preferences (#41138)
- don't emit spurious debug output (#41218)
- Improve the error message for deprecated preferences (#41075)
- Fix MSVC preview version breaking clingo build on Windows (#41185)
- Fix multi-word aliases (#41126)
- Add a warning for unconfigured compiler (#41213)
- environment: fix an issue with deconcretization/reconcretization of specs (#41294)
- buildcache: don't error if a patch is missing, when installing from binaries (#41986)
- Multiple improvements to unit-tests (#41215,#41369,#41495,#41359,#41361,#41345,#41342,#41308,#41226)
## Package updates
- root: add a webgui patch to address security issue (#41404)
- BerkeleyGW: update source urls (#38218)
# v0.21.0 (2023-11-11)
`v0.21.0` is a major feature release.

View File

@@ -37,7 +37,11 @@ to enable reuse for a single installation, and you can use:
spack install --fresh <spec>
to do a fresh install if ``reuse`` is enabled by default.
``reuse: true`` is the default.
``reuse: dependencies`` is the default.
.. seealso::
FAQ: :ref:`Why does Spack pick particular versions and variants? <faq-concretizer-precedence>`
------------------------------------------
Selection of the target microarchitectures
@@ -99,547 +103,3 @@ while `py-numpy` still needs an older version:
Up to Spack v0.20 ``duplicates:strategy:none`` was the default (and only) behavior. From Spack v0.21 the
default behavior is ``duplicates:strategy:minimal``.
.. _build-settings:
================================
Package Settings (packages.yaml)
================================
Spack allows you to customize how your software is built through the
``packages.yaml`` file. Using it, you can make Spack prefer particular
implementations of virtual dependencies (e.g., MPI or BLAS/LAPACK),
or you can make it prefer to build with particular compilers. You can
also tell Spack to use *external* software installations already
present on your system.
At a high level, the ``packages.yaml`` file is structured like this:
.. code-block:: yaml
packages:
package1:
# settings for package1
package2:
# settings for package2
# ...
all:
# settings that apply to all packages.
So you can either set build preferences specifically for *one* package,
or you can specify that certain settings should apply to *all* packages.
The types of settings you can customize are described in detail below.
Spack's build defaults are in the default
``etc/spack/defaults/packages.yaml`` file. You can override them in
``~/.spack/packages.yaml`` or ``etc/spack/packages.yaml``. For more
details on how this works, see :ref:`configuration-scopes`.
.. _sec-external-packages:
-----------------
External Packages
-----------------
Spack can be configured to use externally-installed
packages rather than building its own packages. This may be desirable
if machines ship with system packages, such as a customized MPI
that should be used instead of Spack building its own MPI.
External packages are configured through the ``packages.yaml`` file.
Here's an example of an external configuration:
.. code-block:: yaml
packages:
openmpi:
externals:
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.4.3
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug"
prefix: /opt/openmpi-1.4.3-debug
- spec: "openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.6.5-intel
This example lists three installations of OpenMPI, one built with GCC,
one built with GCC and debug information, and another built with Intel.
If Spack is asked to build a package that uses one of these MPIs as a
dependency, it will use the pre-installed OpenMPI in
the given directory. Note that the specified path is the top-level
install prefix, not the ``bin`` subdirectory.
``packages.yaml`` can also be used to specify modules to load instead
of the installation prefixes. The following example says that module
``CMake/3.7.2`` provides cmake version 3.7.2.
.. code-block:: yaml
cmake:
externals:
- spec: cmake@3.7.2
modules:
- CMake/3.7.2
Each ``packages.yaml`` begins with a ``packages:`` attribute, followed
by a list of package names. To specify externals, add an ``externals:``
attribute under the package name, which lists externals.
Each external should specify a ``spec:`` string that should be as
well-defined as reasonably possible. If a
package lacks a spec component, such as missing a compiler or
package version, then Spack will guess the missing component based
on its most-favored packages, and it may guess incorrectly.
Each package version and compiler listed in an external should
have entries in Spack's packages and compiler configuration, even
though the package and compiler may not ever be built.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Prevent packages from being built from sources
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Adding an external spec in ``packages.yaml`` allows Spack to use an external location,
but it does not prevent Spack from building packages from sources. In the above example,
Spack might choose for many valid reasons to start building and linking with the
latest version of OpenMPI rather than continue using the pre-installed OpenMPI versions.
To prevent this, the ``packages.yaml`` configuration also allows packages
to be flagged as non-buildable. The previous example could be modified to
be:
.. code-block:: yaml
packages:
openmpi:
externals:
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.4.3
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug"
prefix: /opt/openmpi-1.4.3-debug
- spec: "openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.6.5-intel
buildable: False
The addition of the ``buildable`` flag tells Spack that it should never build
its own version of OpenMPI from sources, and it will instead always rely on a pre-built
OpenMPI.
.. note::
If ``concretizer:reuse`` is on (see :ref:`concretizer-options` for more information on that flag)
pre-built specs include specs already available from a local store, an upstream store, a registered
buildcache or specs marked as externals in ``packages.yaml``. If ``concretizer:reuse`` is off, only
external specs in ``packages.yaml`` are included in the list of pre-built specs.
If an external module is specified as not buildable, then Spack will load the
external module into the build environment which can be used for linking.
The ``buildable`` does not need to be paired with external packages.
It could also be used alone to forbid packages that may be
buggy or otherwise undesirable.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Non-buildable virtual packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Virtual packages in Spack can also be specified as not buildable, and
external implementations can be provided. In the example above,
OpenMPI is configured as not buildable, but Spack will often prefer
other MPI implementations over the externally available OpenMPI. Spack
can be configured with every MPI provider not buildable individually,
but more conveniently:
.. code-block:: yaml
packages:
mpi:
buildable: False
openmpi:
externals:
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.4.3
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug"
prefix: /opt/openmpi-1.4.3-debug
- spec: "openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.6.5-intel
Spack can then use any of the listed external implementations of MPI
to satisfy a dependency, and will choose depending on the compiler and
architecture.
In cases where the concretizer is configured to reuse specs, and other ``mpi`` providers
(available via stores or buildcaches) are not wanted, Spack can be configured to require
specs matching only the available externals:
.. code-block:: yaml
packages:
mpi:
buildable: False
require:
- one_of: [
"openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64",
"openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug",
"openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
]
openmpi:
externals:
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.4.3
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug"
prefix: /opt/openmpi-1.4.3-debug
- spec: "openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.6.5-intel
This configuration prevents any spec using MPI and originating from stores or buildcaches to be reused,
unless it matches the requirements under ``packages:mpi:require``. For more information on requirements see
:ref:`package-requirements`.
.. _cmd-spack-external-find:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Automatically Find External Packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can run the :ref:`spack external find <spack-external-find>` command
to search for system-provided packages and add them to ``packages.yaml``.
After running this command your ``packages.yaml`` may include new entries:
.. code-block:: yaml
packages:
cmake:
externals:
- spec: cmake@3.17.2
prefix: /usr
Generally this is useful for detecting a small set of commonly-used packages;
for now this is generally limited to finding build-only dependencies.
Specific limitations include:
* Packages are not discoverable by default: For a package to be
discoverable with ``spack external find``, it needs to add special
logic. See :ref:`here <make-package-findable>` for more details.
* The logic does not search through module files, it can only detect
packages with executables defined in ``PATH``; you can help Spack locate
externals which use module files by loading any associated modules for
packages that you want Spack to know about before running
``spack external find``.
* Spack does not overwrite existing entries in the package configuration:
If there is an external defined for a spec at any configuration scope,
then Spack will not add a new external entry (``spack config blame packages``
can help locate all external entries).
.. _package-requirements:
--------------------
Package Requirements
--------------------
Spack can be configured to always use certain compilers, package
versions, and variants during concretization through package
requirements.
Package requirements are useful when you find yourself repeatedly
specifying the same constraints on the command line, and wish that
Spack respects these constraints whether you mention them explicitly
or not. Another use case is specifying constraints that should apply
to all root specs in an environment, without having to repeat the
constraint everywhere.
Apart from that, requirements config is more flexible than constraints
on the command line, because it can specify constraints on packages
*when they occur* as a dependency. In contrast, on the command line it
is not possible to specify constraints on dependencies while also keeping
those dependencies optional.
^^^^^^^^^^^^^^^^^^^
Requirements syntax
^^^^^^^^^^^^^^^^^^^
The package requirements configuration is specified in ``packages.yaml``,
keyed by package name and expressed using the Spec syntax. In the simplest
case you can specify attributes that you always want the package to have
by providing a single spec string to ``require``:
.. code-block:: yaml
packages:
libfabric:
require: "@1.13.2"
In the above example, ``libfabric`` will always build with version 1.13.2. If you
need to compose multiple configuration scopes ``require`` accepts a list of
strings:
.. code-block:: yaml
packages:
libfabric:
require:
- "@1.13.2"
- "%gcc"
In this case ``libfabric`` will always build with version 1.13.2 **and** using GCC
as a compiler.
For more complex use cases, require accepts also a list of objects. These objects
must have either a ``any_of`` or a ``one_of`` field, containing a list of spec strings,
and they can optionally have a ``when`` and a ``message`` attribute:
.. code-block:: yaml
packages:
openmpi:
require:
- any_of: ["@4.1.5", "%gcc"]
message: "in this example only 4.1.5 can build with other compilers"
``any_of`` is a list of specs. One of those specs must be satisfied
and it is also allowed for the concretized spec to match more than one.
In the above example, that means you could build ``openmpi@4.1.5%gcc``,
``openmpi@4.1.5%clang`` or ``openmpi@3.9%gcc``, but
not ``openmpi@3.9%clang``.
If a custom message is provided, and the requirement is not satisfiable,
Spack will print the custom error message:
.. code-block:: console
$ spack spec openmpi@3.9%clang
==> Error: in this example only 4.1.5 can build with other compilers
We could express a similar requirement using the ``when`` attribute:
.. code-block:: yaml
packages:
openmpi:
require:
- any_of: ["%gcc"]
when: "@:4.1.4"
message: "in this example only 4.1.5 can build with other compilers"
In the example above, if the version turns out to be 4.1.4 or less, we require the compiler to be GCC.
For readability, Spack also allows a ``spec`` key accepting a string when there is only a single
constraint:
.. code-block:: yaml
packages:
openmpi:
require:
- spec: "%gcc"
when: "@:4.1.4"
message: "in this example only 4.1.5 can build with other compilers"
This code snippet and the one before it are semantically equivalent.
Finally, instead of ``any_of`` you can use ``one_of`` which also takes a list of specs. The final
concretized spec must match one and only one of them:
.. code-block:: yaml
packages:
mpich:
require:
- one_of: ["+cuda", "+rocm"]
In the example above, that means you could build ``mpich+cuda`` or ``mpich+rocm`` but not ``mpich+cuda+rocm``.
.. note::
For ``any_of`` and ``one_of``, the order of specs indicates a
preference: items that appear earlier in the list are preferred
(note that these preferences can be ignored in favor of others).
.. note::
When using a conditional requirement, Spack is allowed to actively avoid the triggering
condition (the ``when=...`` spec) if that leads to a concrete spec with better scores in
the optimization criteria. To check the current optimization criteria and their
priorities you can run ``spack solve zlib``.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Setting default requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can also set default requirements for all packages under ``all``
like this:
.. code-block:: yaml
packages:
all:
require: '%clang'
which means every spec will be required to use ``clang`` as a compiler.
Note that in this case ``all`` represents a *default set of requirements* -
if there are specific package requirements, then the default requirements
under ``all`` are disregarded. For example, with a configuration like this:
.. code-block:: yaml
packages:
all:
require: '%clang'
cmake:
require: '%gcc'
Spack requires ``cmake`` to use ``gcc`` and all other nodes (including ``cmake``
dependencies) to use ``clang``.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Setting requirements on virtual specs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A requirement on a virtual spec applies whenever that virtual is present in the DAG.
This can be useful for fixing which virtual provider you want to use:
.. code-block:: yaml
packages:
mpi:
require: 'mvapich2 %gcc'
With the configuration above the only allowed ``mpi`` provider is ``mvapich2 %gcc``.
Requirements on the virtual spec and on the specific provider are both applied, if
present. For instance with a configuration like:
.. code-block:: yaml
packages:
mpi:
require: 'mvapich2 %gcc'
mvapich2:
require: '~cuda'
you will use ``mvapich2~cuda %gcc`` as an ``mpi`` provider.
.. _package-preferences:
-------------------
Package Preferences
-------------------
In some cases package requirements can be too strong, and package
preferences are the better option. Package preferences do not impose
constraints on packages for particular versions or variants values,
they rather only set defaults. The concretizer is free to change
them if it must, due to other constraints, and also prefers reusing
installed packages over building new ones that are a better match for
preferences.
Most package preferences (``compilers``, ``target`` and ``providers``)
can only be set globally under the ``all`` section of ``packages.yaml``:
.. code-block:: yaml
packages:
all:
compiler: [gcc@12.2.0, clang@12:, oneapi@2023:]
target: [x86_64_v3]
providers:
mpi: [mvapich2, mpich, openmpi]
These preferences override Spack's default and effectively reorder priorities
when looking for the best compiler, target or virtual package provider. Each
preference takes an ordered list of spec constraints, with earlier entries in
the list being preferred over later entries.
In the example above all packages prefer to be compiled with ``gcc@12.2.0``,
to target the ``x86_64_v3`` microarchitecture and to use ``mvapich2`` if they
depend on ``mpi``.
The ``variants`` and ``version`` preferences can be set under
package specific sections of the ``packages.yaml`` file:
.. code-block:: yaml
packages:
opencv:
variants: +debug
gperftools:
version: [2.2, 2.4, 2.3]
In this case, the preference for ``opencv`` is to build with debug options, while
``gperftools`` prefers version 2.2 over 2.4.
Any preference can be overwritten on the command line if explicitly requested.
Preferences cannot overcome explicit constraints, as they only set a preferred
ordering among homogeneous attribute values. Going back to the example, if
``gperftools@2.3:`` was requested, then Spack will install version 2.4
since the most preferred version 2.2 is prohibited by the version constraint.
.. _package_permissions:
-------------------
Package Permissions
-------------------
Spack can be configured to assign permissions to the files installed
by a package.
In the ``packages.yaml`` file under ``permissions``, the attributes
``read``, ``write``, and ``group`` control the package
permissions. These attributes can be set per-package, or for all
packages under ``all``. If permissions are set under ``all`` and for a
specific package, the package-specific settings take precedence.
The ``read`` and ``write`` attributes take one of ``user``, ``group``,
and ``world``.
.. code-block:: yaml
packages:
all:
permissions:
write: group
group: spack
my_app:
permissions:
read: group
group: my_team
The permissions settings describe the broadest level of access to
installations of the specified packages. The execute permissions of
the file are set to the same level as read permissions for those files
that are executable. The default setting for ``read`` is ``world``,
and for ``write`` is ``user``. In the example above, installations of
``my_app`` will be installed with user and group permissions but no
world permissions, and owned by the group ``my_team``. All other
packages will be installed with user and group write privileges, and
world read privileges. Those packages will be owned by the group
``spack``.
The ``group`` attribute assigns a Unix-style group to a package. All
files installed by the package will be owned by the assigned group,
and the sticky group bit will be set on the install prefix and all
directories inside the install prefix. This will ensure that even
manually placed files within the install prefix are owned by the
assigned group. If no group is assigned, Spack will allow the OS
default behavior to go as expected.
----------------------------
Assigning Package Attributes
----------------------------
You can assign class-level attributes in the configuration:
.. code-block:: yaml
packages:
mpileaks:
# Override existing attributes
url: http://www.somewhereelse.com/mpileaks-1.0.tar.gz
# ... or add new ones
x: 1
Attributes set this way will be accessible to any method executed
in the package.py file (e.g. the ``install()`` method). Values for these
attributes may be any value parseable by yaml.
These can only be applied to specific packages, not "all" or
virtual packages.

View File

@@ -392,7 +392,7 @@ See section
:ref:`Configuration Scopes <configuration-scopes>`
for an explanation about the different files
and section
:ref:`Build customization <build-settings>`
:ref:`Build customization <packages-config>`
for specifics and examples for ``packages.yaml`` files.
.. If your system administrator did not provide modules for pre-installed Intel

View File

@@ -17,7 +17,7 @@ case you want to skip directly to specific docs:
* :ref:`config.yaml <config-yaml>`
* :ref:`mirrors.yaml <mirrors>`
* :ref:`modules.yaml <modules>`
* :ref:`packages.yaml <build-settings>`
* :ref:`packages.yaml <packages-config>`
* :ref:`repos.yaml <repositories>`
You can also add any of these as inline configuration in the YAML

View File

@@ -0,0 +1,77 @@
.. Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
Spack Project Developers. See the top-level COPYRIGHT file for details.
SPDX-License-Identifier: (Apache-2.0 OR MIT)
==========================
Frequently Asked Questions
==========================
This page contains answers to frequently asked questions about Spack.
If you have questions that are not answered here, feel free to ask on
`Slack <https://slack.spack.io>`_ or `GitHub Discussions
<https://github.com/spack/spack/discussions>`_. If you've learned the
answer to a question that you think should be here, please consider
contributing to this page.
.. _faq-concretizer-precedence:
-----------------------------------------------------
Why does Spack pick particular versions and variants?
-----------------------------------------------------
This question comes up in a variety of forms:
1. Why does Spack seem to ignore my package preferences from ``packages.yaml`` config?
2. Why does Spack toggle a variant instead of using the default from the ``package.py`` file?
The short answer is that Spack always picks an optimal configuration
based on a complex set of criteria\ [#f1]_. These criteria are more nuanced
than always choosing the latest versions or default variants.
.. note::
As a rule of thumb: requirements + constraints > reuse > preferences > defaults.
The following set of criteria (from lowest to highest precedence) explain
common cases where concretization output may seem surprising at first.
1. :ref:`Package preferences <package-preferences>` configured in ``packages.yaml``
override variant defaults from ``package.py`` files, and influence the optimal
ordering of versions. Preferences are specified as follows:
.. code-block:: yaml
packages:
foo:
version: [1.0, 1.1]
variants: ~mpi
2. :ref:`Reuse concretization <concretizer-options>` configured in ``concretizer.yaml``
overrides preferences, since it's typically faster to reuse an existing spec than to
build a preferred one from sources. When build caches are enabled, specs may be reused
from a remote location too. Reuse concretization is configured as follows:
.. code-block:: yaml
concretizer:
reuse: dependencies # other options are 'true' and 'false'
3. :ref:`Package requirements <package-requirements>` configured in ``packages.yaml``,
and constraints from the command line as well as ``package.py`` files override all
of the above. Requirements are specified as follows:
.. code-block:: yaml
packages:
foo:
require:
- "@1.2: +mpi"
Requirements and constraints restrict the set of possible solutions, while reuse
behavior and preferences influence what an optimal solution looks like.
.. rubric:: Footnotes
.. [#f1] The exact list of criteria can be retrieved with the ``spack solve`` command

View File

@@ -55,6 +55,7 @@ or refer to the full manual below.
getting_started
basic_usage
replace_conda_homebrew
frequently_asked_questions
.. toctree::
:maxdepth: 2
@@ -70,7 +71,7 @@ or refer to the full manual below.
configuration
config_yaml
bootstrapping
packages_yaml
build_settings
environments
containers
@@ -78,6 +79,7 @@ or refer to the full manual below.
module_file_support
repositories
binary_caches
bootstrapping
command_index
chain
extensions

View File

@@ -0,0 +1,559 @@
.. Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
Spack Project Developers. See the top-level COPYRIGHT file for details.
SPDX-License-Identifier: (Apache-2.0 OR MIT)
.. _packages-config:
================================
Package Settings (packages.yaml)
================================
Spack allows you to customize how your software is built through the
``packages.yaml`` file. Using it, you can make Spack prefer particular
implementations of virtual dependencies (e.g., MPI or BLAS/LAPACK),
or you can make it prefer to build with particular compilers. You can
also tell Spack to use *external* software installations already
present on your system.
At a high level, the ``packages.yaml`` file is structured like this:
.. code-block:: yaml
packages:
package1:
# settings for package1
package2:
# settings for package2
# ...
all:
# settings that apply to all packages.
So you can either set build preferences specifically for *one* package,
or you can specify that certain settings should apply to *all* packages.
The types of settings you can customize are described in detail below.
Spack's build defaults are in the default
``etc/spack/defaults/packages.yaml`` file. You can override them in
``~/.spack/packages.yaml`` or ``etc/spack/packages.yaml``. For more
details on how this works, see :ref:`configuration-scopes`.
.. _sec-external-packages:
-----------------
External Packages
-----------------
Spack can be configured to use externally-installed
packages rather than building its own packages. This may be desirable
if machines ship with system packages, such as a customized MPI
that should be used instead of Spack building its own MPI.
External packages are configured through the ``packages.yaml`` file.
Here's an example of an external configuration:
.. code-block:: yaml
packages:
openmpi:
externals:
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.4.3
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug"
prefix: /opt/openmpi-1.4.3-debug
- spec: "openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.6.5-intel
This example lists three installations of OpenMPI, one built with GCC,
one built with GCC and debug information, and another built with Intel.
If Spack is asked to build a package that uses one of these MPIs as a
dependency, it will use the pre-installed OpenMPI in
the given directory. Note that the specified path is the top-level
install prefix, not the ``bin`` subdirectory.
``packages.yaml`` can also be used to specify modules to load instead
of the installation prefixes. The following example says that module
``CMake/3.7.2`` provides cmake version 3.7.2.
.. code-block:: yaml
cmake:
externals:
- spec: cmake@3.7.2
modules:
- CMake/3.7.2
Each ``packages.yaml`` begins with a ``packages:`` attribute, followed
by a list of package names. To specify externals, add an ``externals:``
attribute under the package name, which lists externals.
Each external should specify a ``spec:`` string that should be as
well-defined as reasonably possible. If a
package lacks a spec component, such as missing a compiler or
package version, then Spack will guess the missing component based
on its most-favored packages, and it may guess incorrectly.
Each package version and compiler listed in an external should
have entries in Spack's packages and compiler configuration, even
though the package and compiler may not ever be built.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Prevent packages from being built from sources
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Adding an external spec in ``packages.yaml`` allows Spack to use an external location,
but it does not prevent Spack from building packages from sources. In the above example,
Spack might choose for many valid reasons to start building and linking with the
latest version of OpenMPI rather than continue using the pre-installed OpenMPI versions.
To prevent this, the ``packages.yaml`` configuration also allows packages
to be flagged as non-buildable. The previous example could be modified to
be:
.. code-block:: yaml
packages:
openmpi:
externals:
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.4.3
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug"
prefix: /opt/openmpi-1.4.3-debug
- spec: "openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.6.5-intel
buildable: False
The addition of the ``buildable`` flag tells Spack that it should never build
its own version of OpenMPI from sources, and it will instead always rely on a pre-built
OpenMPI.
.. note::
If ``concretizer:reuse`` is on (see :ref:`concretizer-options` for more information on that flag)
pre-built specs include specs already available from a local store, an upstream store, a registered
buildcache or specs marked as externals in ``packages.yaml``. If ``concretizer:reuse`` is off, only
external specs in ``packages.yaml`` are included in the list of pre-built specs.
If an external module is specified as not buildable, then Spack will load the
external module into the build environment which can be used for linking.
The ``buildable`` does not need to be paired with external packages.
It could also be used alone to forbid packages that may be
buggy or otherwise undesirable.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Non-buildable virtual packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Virtual packages in Spack can also be specified as not buildable, and
external implementations can be provided. In the example above,
OpenMPI is configured as not buildable, but Spack will often prefer
other MPI implementations over the externally available OpenMPI. Spack
can be configured with every MPI provider not buildable individually,
but more conveniently:
.. code-block:: yaml
packages:
mpi:
buildable: False
openmpi:
externals:
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.4.3
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug"
prefix: /opt/openmpi-1.4.3-debug
- spec: "openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.6.5-intel
Spack can then use any of the listed external implementations of MPI
to satisfy a dependency, and will choose depending on the compiler and
architecture.
In cases where the concretizer is configured to reuse specs, and other ``mpi`` providers
(available via stores or buildcaches) are not wanted, Spack can be configured to require
specs matching only the available externals:
.. code-block:: yaml
packages:
mpi:
buildable: False
require:
- one_of: [
"openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64",
"openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug",
"openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
]
openmpi:
externals:
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.4.3
- spec: "openmpi@1.4.3%gcc@4.4.7 arch=linux-debian7-x86_64+debug"
prefix: /opt/openmpi-1.4.3-debug
- spec: "openmpi@1.6.5%intel@10.1 arch=linux-debian7-x86_64"
prefix: /opt/openmpi-1.6.5-intel
This configuration prevents any spec using MPI and originating from stores or buildcaches to be reused,
unless it matches the requirements under ``packages:mpi:require``. For more information on requirements see
:ref:`package-requirements`.
.. _cmd-spack-external-find:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Automatically Find External Packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can run the :ref:`spack external find <spack-external-find>` command
to search for system-provided packages and add them to ``packages.yaml``.
After running this command your ``packages.yaml`` may include new entries:
.. code-block:: yaml
packages:
cmake:
externals:
- spec: cmake@3.17.2
prefix: /usr
Generally this is useful for detecting a small set of commonly-used packages;
for now this is generally limited to finding build-only dependencies.
Specific limitations include:
* Packages are not discoverable by default: For a package to be
discoverable with ``spack external find``, it needs to add special
logic. See :ref:`here <make-package-findable>` for more details.
* The logic does not search through module files, it can only detect
packages with executables defined in ``PATH``; you can help Spack locate
externals which use module files by loading any associated modules for
packages that you want Spack to know about before running
``spack external find``.
* Spack does not overwrite existing entries in the package configuration:
If there is an external defined for a spec at any configuration scope,
then Spack will not add a new external entry (``spack config blame packages``
can help locate all external entries).
.. _package-requirements:
--------------------
Package Requirements
--------------------
Spack can be configured to always use certain compilers, package
versions, and variants during concretization through package
requirements.
Package requirements are useful when you find yourself repeatedly
specifying the same constraints on the command line, and wish that
Spack respects these constraints whether you mention them explicitly
or not. Another use case is specifying constraints that should apply
to all root specs in an environment, without having to repeat the
constraint everywhere.
Apart from that, requirements config is more flexible than constraints
on the command line, because it can specify constraints on packages
*when they occur* as a dependency. In contrast, on the command line it
is not possible to specify constraints on dependencies while also keeping
those dependencies optional.
.. seealso::
FAQ: :ref:`Why does Spack pick particular versions and variants? <faq-concretizer-precedence>`
^^^^^^^^^^^^^^^^^^^
Requirements syntax
^^^^^^^^^^^^^^^^^^^
The package requirements configuration is specified in ``packages.yaml``,
keyed by package name and expressed using the Spec syntax. In the simplest
case you can specify attributes that you always want the package to have
by providing a single spec string to ``require``:
.. code-block:: yaml
packages:
libfabric:
require: "@1.13.2"
In the above example, ``libfabric`` will always build with version 1.13.2. If you
need to compose multiple configuration scopes ``require`` accepts a list of
strings:
.. code-block:: yaml
packages:
libfabric:
require:
- "@1.13.2"
- "%gcc"
In this case ``libfabric`` will always build with version 1.13.2 **and** using GCC
as a compiler.
For more complex use cases, require accepts also a list of objects. These objects
must have either a ``any_of`` or a ``one_of`` field, containing a list of spec strings,
and they can optionally have a ``when`` and a ``message`` attribute:
.. code-block:: yaml
packages:
openmpi:
require:
- any_of: ["@4.1.5", "%gcc"]
message: "in this example only 4.1.5 can build with other compilers"
``any_of`` is a list of specs. One of those specs must be satisfied
and it is also allowed for the concretized spec to match more than one.
In the above example, that means you could build ``openmpi@4.1.5%gcc``,
``openmpi@4.1.5%clang`` or ``openmpi@3.9%gcc``, but
not ``openmpi@3.9%clang``.
If a custom message is provided, and the requirement is not satisfiable,
Spack will print the custom error message:
.. code-block:: console
$ spack spec openmpi@3.9%clang
==> Error: in this example only 4.1.5 can build with other compilers
We could express a similar requirement using the ``when`` attribute:
.. code-block:: yaml
packages:
openmpi:
require:
- any_of: ["%gcc"]
when: "@:4.1.4"
message: "in this example only 4.1.5 can build with other compilers"
In the example above, if the version turns out to be 4.1.4 or less, we require the compiler to be GCC.
For readability, Spack also allows a ``spec`` key accepting a string when there is only a single
constraint:
.. code-block:: yaml
packages:
openmpi:
require:
- spec: "%gcc"
when: "@:4.1.4"
message: "in this example only 4.1.5 can build with other compilers"
This code snippet and the one before it are semantically equivalent.
Finally, instead of ``any_of`` you can use ``one_of`` which also takes a list of specs. The final
concretized spec must match one and only one of them:
.. code-block:: yaml
packages:
mpich:
require:
- one_of: ["+cuda", "+rocm"]
In the example above, that means you could build ``mpich+cuda`` or ``mpich+rocm`` but not ``mpich+cuda+rocm``.
.. note::
For ``any_of`` and ``one_of``, the order of specs indicates a
preference: items that appear earlier in the list are preferred
(note that these preferences can be ignored in favor of others).
.. note::
When using a conditional requirement, Spack is allowed to actively avoid the triggering
condition (the ``when=...`` spec) if that leads to a concrete spec with better scores in
the optimization criteria. To check the current optimization criteria and their
priorities you can run ``spack solve zlib``.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Setting default requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can also set default requirements for all packages under ``all``
like this:
.. code-block:: yaml
packages:
all:
require: '%clang'
which means every spec will be required to use ``clang`` as a compiler.
Note that in this case ``all`` represents a *default set of requirements* -
if there are specific package requirements, then the default requirements
under ``all`` are disregarded. For example, with a configuration like this:
.. code-block:: yaml
packages:
all:
require: '%clang'
cmake:
require: '%gcc'
Spack requires ``cmake`` to use ``gcc`` and all other nodes (including ``cmake``
dependencies) to use ``clang``.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Setting requirements on virtual specs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A requirement on a virtual spec applies whenever that virtual is present in the DAG.
This can be useful for fixing which virtual provider you want to use:
.. code-block:: yaml
packages:
mpi:
require: 'mvapich2 %gcc'
With the configuration above the only allowed ``mpi`` provider is ``mvapich2 %gcc``.
Requirements on the virtual spec and on the specific provider are both applied, if
present. For instance with a configuration like:
.. code-block:: yaml
packages:
mpi:
require: 'mvapich2 %gcc'
mvapich2:
require: '~cuda'
you will use ``mvapich2~cuda %gcc`` as an ``mpi`` provider.
.. _package-preferences:
-------------------
Package Preferences
-------------------
In some cases package requirements can be too strong, and package
preferences are the better option. Package preferences do not impose
constraints on packages for particular versions or variants values,
they rather only set defaults. The concretizer is free to change
them if it must, due to other constraints, and also prefers reusing
installed packages over building new ones that are a better match for
preferences.
.. seealso::
FAQ: :ref:`Why does Spack pick particular versions and variants? <faq-concretizer-precedence>`
Most package preferences (``compilers``, ``target`` and ``providers``)
can only be set globally under the ``all`` section of ``packages.yaml``:
.. code-block:: yaml
packages:
all:
compiler: [gcc@12.2.0, clang@12:, oneapi@2023:]
target: [x86_64_v3]
providers:
mpi: [mvapich2, mpich, openmpi]
These preferences override Spack's default and effectively reorder priorities
when looking for the best compiler, target or virtual package provider. Each
preference takes an ordered list of spec constraints, with earlier entries in
the list being preferred over later entries.
In the example above all packages prefer to be compiled with ``gcc@12.2.0``,
to target the ``x86_64_v3`` microarchitecture and to use ``mvapich2`` if they
depend on ``mpi``.
The ``variants`` and ``version`` preferences can be set under
package specific sections of the ``packages.yaml`` file:
.. code-block:: yaml
packages:
opencv:
variants: +debug
gperftools:
version: [2.2, 2.4, 2.3]
In this case, the preference for ``opencv`` is to build with debug options, while
``gperftools`` prefers version 2.2 over 2.4.
Any preference can be overwritten on the command line if explicitly requested.
Preferences cannot overcome explicit constraints, as they only set a preferred
ordering among homogeneous attribute values. Going back to the example, if
``gperftools@2.3:`` was requested, then Spack will install version 2.4
since the most preferred version 2.2 is prohibited by the version constraint.
.. _package_permissions:
-------------------
Package Permissions
-------------------
Spack can be configured to assign permissions to the files installed
by a package.
In the ``packages.yaml`` file under ``permissions``, the attributes
``read``, ``write``, and ``group`` control the package
permissions. These attributes can be set per-package, or for all
packages under ``all``. If permissions are set under ``all`` and for a
specific package, the package-specific settings take precedence.
The ``read`` and ``write`` attributes take one of ``user``, ``group``,
and ``world``.
.. code-block:: yaml
packages:
all:
permissions:
write: group
group: spack
my_app:
permissions:
read: group
group: my_team
The permissions settings describe the broadest level of access to
installations of the specified packages. The execute permissions of
the file are set to the same level as read permissions for those files
that are executable. The default setting for ``read`` is ``world``,
and for ``write`` is ``user``. In the example above, installations of
``my_app`` will be installed with user and group permissions but no
world permissions, and owned by the group ``my_team``. All other
packages will be installed with user and group write privileges, and
world read privileges. Those packages will be owned by the group
``spack``.
The ``group`` attribute assigns a Unix-style group to a package. All
files installed by the package will be owned by the assigned group,
and the sticky group bit will be set on the install prefix and all
directories inside the install prefix. This will ensure that even
manually placed files within the install prefix are owned by the
assigned group. If no group is assigned, Spack will allow the OS
default behavior to go as expected.
----------------------------
Assigning Package Attributes
----------------------------
You can assign class-level attributes in the configuration:
.. code-block:: yaml
packages:
mpileaks:
# Override existing attributes
url: http://www.somewhereelse.com/mpileaks-1.0.tar.gz
# ... or add new ones
x: 1
Attributes set this way will be accessible to any method executed
in the package.py file (e.g. the ``install()`` method). Values for these
attributes may be any value parseable by yaml.
These can only be applied to specific packages, not "all" or
virtual packages.

View File

@@ -4,7 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
#: PEP440 canonical <major>.<minor>.<micro>.<devN> string
__version__ = "0.21.0"
__version__ = "0.21.1"
spack_version = __version__

View File

@@ -40,6 +40,7 @@ def _search_duplicate_compilers(error_cls):
import collections.abc
import glob
import inspect
import io
import itertools
import pathlib
import pickle
@@ -54,6 +55,7 @@ def _search_duplicate_compilers(error_cls):
import spack.repo
import spack.spec
import spack.util.crypto
import spack.util.spack_yaml as syaml
import spack.variant
#: Map an audit tag to a list of callables implementing checks
@@ -250,6 +252,88 @@ def _search_duplicate_specs_in_externals(error_cls):
return errors
@config_packages
def _deprecated_preferences(error_cls):
"""Search package preferences deprecated in v0.21 (and slated for removal in v0.22)"""
# TODO (v0.22): remove this audit as the attributes will not be allowed in config
errors = []
packages_yaml = spack.config.CONFIG.get_config("packages")
def make_error(attribute_name, config_data, summary):
s = io.StringIO()
s.write("Occurring in the following file:\n")
dict_view = syaml.syaml_dict((k, v) for k, v in config_data.items() if k == attribute_name)
syaml.dump_config(dict_view, stream=s, blame=True)
return error_cls(summary=summary, details=[s.getvalue()])
if "all" in packages_yaml and "version" in packages_yaml["all"]:
summary = "Using the deprecated 'version' attribute under 'packages:all'"
errors.append(make_error("version", packages_yaml["all"], summary))
for package_name in packages_yaml:
if package_name == "all":
continue
package_conf = packages_yaml[package_name]
for attribute in ("compiler", "providers", "target"):
if attribute not in package_conf:
continue
summary = (
f"Using the deprecated '{attribute}' attribute " f"under 'packages:{package_name}'"
)
errors.append(make_error(attribute, package_conf, summary))
return errors
@config_packages
def _avoid_mismatched_variants(error_cls):
"""Warns if variant preferences have mismatched types or names."""
errors = []
packages_yaml = spack.config.CONFIG.get_config("packages")
def make_error(config_data, summary):
s = io.StringIO()
s.write("Occurring in the following file:\n")
syaml.dump_config(config_data, stream=s, blame=True)
return error_cls(summary=summary, details=[s.getvalue()])
for pkg_name in packages_yaml:
# 'all:' must be more forgiving, since it is setting defaults for everything
if pkg_name == "all" or "variants" not in packages_yaml[pkg_name]:
continue
preferences = packages_yaml[pkg_name]["variants"]
if not isinstance(preferences, list):
preferences = [preferences]
for variants in preferences:
current_spec = spack.spec.Spec(variants)
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
for variant in current_spec.variants.values():
# Variant does not exist at all
if variant.name not in pkg_cls.variants:
summary = (
f"Setting a preference for the '{pkg_name}' package to the "
f"non-existing variant '{variant.name}'"
)
errors.append(make_error(preferences, summary))
continue
# Variant cannot accept this value
s = spack.spec.Spec(pkg_name)
try:
s.update_variant_validate(variant.name, variant.value)
except Exception:
summary = (
f"Setting the variant '{variant.name}' of the '{pkg_name}' package "
f"to the invalid value '{str(variant)}'"
)
errors.append(make_error(preferences, summary))
return errors
#: Sanity checks on package directives
package_directives = AuditClass(
group="packages",
@@ -776,7 +860,7 @@ def _version_constraints_are_satisfiable_by_some_version_in_repo(pkgs, error_cls
)
except Exception:
summary = (
"{0}: dependency on {1} cannot be satisfied " "by known versions of {1.name}"
"{0}: dependency on {1} cannot be satisfied by known versions of {1.name}"
).format(pkg_name, s)
details = ["happening in " + filename]
if dependency_pkg_cls is not None:
@@ -818,6 +902,53 @@ def _analyze_variants_in_directive(pkg, constraint, directive, error_cls):
return errors
@package_directives
def _named_specs_in_when_arguments(pkgs, error_cls):
"""Reports named specs in the 'when=' attribute of a directive.
Note that 'conflicts' is the only directive allowing that.
"""
errors = []
for pkg_name in pkgs:
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
def _extracts_errors(triggers, summary):
_errors = []
for trigger in list(triggers):
when_spec = spack.spec.Spec(trigger)
if when_spec.name is not None and when_spec.name != pkg_name:
details = [f"using '{trigger}', should be '^{trigger}'"]
_errors.append(error_cls(summary=summary, details=details))
return _errors
for dname, triggers in pkg_cls.dependencies.items():
summary = f"{pkg_name}: wrong 'when=' condition for the '{dname}' dependency"
errors.extend(_extracts_errors(triggers, summary))
for vname, (variant, triggers) in pkg_cls.variants.items():
summary = f"{pkg_name}: wrong 'when=' condition for the '{vname}' variant"
errors.extend(_extracts_errors(triggers, summary))
for provided, triggers in pkg_cls.provided.items():
summary = f"{pkg_name}: wrong 'when=' condition for the '{provided}' virtual"
errors.extend(_extracts_errors(triggers, summary))
for _, triggers in pkg_cls.requirements.items():
triggers = [when_spec for when_spec, _, _ in triggers]
summary = f"{pkg_name}: wrong 'when=' condition in 'requires' directive"
errors.extend(_extracts_errors(triggers, summary))
triggers = list(pkg_cls.patches)
summary = f"{pkg_name}: wrong 'when=' condition in 'patch' directives"
errors.extend(_extracts_errors(triggers, summary))
triggers = list(pkg_cls.resources)
summary = f"{pkg_name}: wrong 'when=' condition in 'resource' directives"
errors.extend(_extracts_errors(triggers, summary))
return llnl.util.lang.dedupe(errors)
#: Sanity checks on package directives
external_detection = AuditClass(
group="externals",

View File

@@ -69,6 +69,7 @@
BUILD_CACHE_RELATIVE_PATH = "build_cache"
BUILD_CACHE_KEYS_RELATIVE_PATH = "_pgp"
CURRENT_BUILD_CACHE_LAYOUT_VERSION = 1
FORWARD_COMPAT_BUILD_CACHE_LAYOUT_VERSION = 2
class BuildCacheDatabase(spack_db.Database):
@@ -1696,7 +1697,7 @@ def download_tarball(spec, unsigned=False, mirrors_for_spec=None):
try:
_get_valid_spec_file(
local_specfile_stage.save_filename,
CURRENT_BUILD_CACHE_LAYOUT_VERSION,
FORWARD_COMPAT_BUILD_CACHE_LAYOUT_VERSION,
)
except InvalidMetadataFile as e:
tty.warn(
@@ -1737,7 +1738,7 @@ def download_tarball(spec, unsigned=False, mirrors_for_spec=None):
try:
_get_valid_spec_file(
local_specfile_path, CURRENT_BUILD_CACHE_LAYOUT_VERSION
local_specfile_path, FORWARD_COMPAT_BUILD_CACHE_LAYOUT_VERSION
)
except InvalidMetadataFile as e:
tty.warn(
@@ -2026,11 +2027,12 @@ def _extract_inner_tarball(spec, filename, extract_to, unsigned, remote_checksum
def _tar_strip_component(tar: tarfile.TarFile, prefix: str):
"""Strip the top-level directory `prefix` from the member names in a tarfile."""
"""Yield all members of tarfile that start with given prefix, and strip that prefix (including
symlinks)"""
# Including trailing /, otherwise we end up with absolute paths.
regex = re.compile(re.escape(prefix) + "/*")
# Remove the top-level directory from the member (link)names.
# Only yield members in the package prefix.
# Note: when a tarfile is created, relative in-prefix symlinks are
# expanded to matching member names of tarfile entries. So, we have
# to ensure that those are updated too.
@@ -2038,12 +2040,14 @@ def _tar_strip_component(tar: tarfile.TarFile, prefix: str):
# them.
for m in tar.getmembers():
result = regex.match(m.name)
assert result is not None
if not result:
continue
m.name = m.name[result.end() :]
if m.linkname:
result = regex.match(m.linkname)
if result:
m.linkname = m.linkname[result.end() :]
yield m
def extract_tarball(spec, download_result, unsigned=False, force=False, timer=timer.NULL_TIMER):
@@ -2067,7 +2071,7 @@ def extract_tarball(spec, download_result, unsigned=False, force=False, timer=ti
specfile_path = download_result["specfile_stage"].save_filename
spec_dict, layout_version = _get_valid_spec_file(
specfile_path, CURRENT_BUILD_CACHE_LAYOUT_VERSION
specfile_path, FORWARD_COMPAT_BUILD_CACHE_LAYOUT_VERSION
)
bchecksum = spec_dict["binary_cache_checksum"]
@@ -2086,7 +2090,7 @@ def extract_tarball(spec, download_result, unsigned=False, force=False, timer=ti
_delete_staged_downloads(download_result)
shutil.rmtree(tmpdir)
raise e
elif layout_version == 1:
elif 1 <= layout_version <= 2:
# Newer buildcache layout: the .spack file contains just
# in the install tree, the signature, if it exists, is
# wrapped around the spec.json at the root. If sig verify
@@ -2113,8 +2117,10 @@ def extract_tarball(spec, download_result, unsigned=False, force=False, timer=ti
try:
with closing(tarfile.open(tarfile_path, "r")) as tar:
# Remove install prefix from tarfil to extract directly into spec.prefix
_tar_strip_component(tar, prefix=_ensure_common_prefix(tar))
tar.extractall(path=spec.prefix)
tar.extractall(
path=spec.prefix,
members=_tar_strip_component(tar, prefix=_ensure_common_prefix(tar)),
)
except Exception:
shutil.rmtree(spec.prefix, ignore_errors=True)
_delete_staged_downloads(download_result)
@@ -2149,20 +2155,47 @@ def extract_tarball(spec, download_result, unsigned=False, force=False, timer=ti
def _ensure_common_prefix(tar: tarfile.TarFile) -> str:
# Get the shortest length directory.
common_prefix = min((e.name for e in tar.getmembers() if e.isdir()), key=len, default=None)
# Find the lowest `binary_distribution` file (hard-coded forward slash is on purpose).
binary_distribution = min(
(
e.name
for e in tar.getmembers()
if e.isfile() and e.name.endswith(".spack/binary_distribution")
),
key=len,
default=None,
)
if common_prefix is None:
raise ValueError("Tarball does not contain a common prefix")
if binary_distribution is None:
raise ValueError("Tarball is not a Spack package, missing binary_distribution file")
# Validate that each file starts with the prefix
pkg_path = pathlib.PurePosixPath(binary_distribution).parent.parent
# Even the most ancient Spack version has required to list the dir of the package itself, so
# guard against broken tarballs where `path.parent.parent` is empty.
if pkg_path == pathlib.PurePosixPath():
raise ValueError("Invalid tarball, missing package prefix dir")
pkg_prefix = str(pkg_path)
# Ensure all tar entries are in the pkg_prefix dir, and if they're not, they should be parent
# dirs of it.
has_prefix = False
for member in tar.getmembers():
if not member.name.startswith(common_prefix):
raise ValueError(
f"Tarball contains file {member.name} outside of prefix {common_prefix}"
)
stripped = member.name.rstrip("/")
if not (
stripped.startswith(pkg_prefix) or member.isdir() and pkg_prefix.startswith(stripped)
):
raise ValueError(f"Tarball contains file {stripped} outside of prefix {pkg_prefix}")
if member.isdir() and stripped == pkg_prefix:
has_prefix = True
return common_prefix
# This is technically not required, but let's be defensive about the existence of the package
# prefix dir.
if not has_prefix:
raise ValueError(f"Tarball does not contain a common prefix {pkg_prefix}")
return pkg_prefix
def install_root_node(spec, unsigned=False, force=False, sha256=None):

View File

@@ -213,7 +213,8 @@ def _root_spec(spec_str: str) -> str:
if str(spack.platforms.host()) == "darwin":
spec_str += " %apple-clang"
elif str(spack.platforms.host()) == "windows":
spec_str += " %msvc"
# TODO (johnwparent): Remove version constraint when clingo patch is up
spec_str += " %msvc@:19.37"
else:
spec_str += " %gcc"

View File

@@ -324,19 +324,29 @@ def set_compiler_environment_variables(pkg, env):
# ttyout, ttyerr, etc.
link_dir = spack.paths.build_env_path
# Set SPACK compiler variables so that our wrapper knows what to call
# Set SPACK compiler variables so that our wrapper knows what to
# call. If there is no compiler configured then use a default
# wrapper which will emit an error if it is used.
if compiler.cc:
env.set("SPACK_CC", compiler.cc)
env.set("CC", os.path.join(link_dir, compiler.link_paths["cc"]))
else:
env.set("CC", os.path.join(link_dir, "cc"))
if compiler.cxx:
env.set("SPACK_CXX", compiler.cxx)
env.set("CXX", os.path.join(link_dir, compiler.link_paths["cxx"]))
else:
env.set("CC", os.path.join(link_dir, "c++"))
if compiler.f77:
env.set("SPACK_F77", compiler.f77)
env.set("F77", os.path.join(link_dir, compiler.link_paths["f77"]))
else:
env.set("F77", os.path.join(link_dir, "f77"))
if compiler.fc:
env.set("SPACK_FC", compiler.fc)
env.set("FC", os.path.join(link_dir, compiler.link_paths["fc"]))
else:
env.set("FC", os.path.join(link_dir, "fc"))
# Set SPACK compiler rpath flags so that our wrapper knows what to use
env.set("SPACK_CC_RPATH_ARG", compiler.cc_rpath_arg)
@@ -743,15 +753,16 @@ def setup_package(pkg, dirty, context: Context = Context.BUILD):
set_compiler_environment_variables(pkg, env_mods)
set_wrapper_variables(pkg, env_mods)
tty.debug("setup_package: grabbing modifications from dependencies")
env_mods.extend(setup_context.get_env_modifications())
tty.debug("setup_package: collected all modifications from dependencies")
# architecture specific setup
# Platform specific setup goes before package specific setup. This is for setting
# defaults like MACOSX_DEPLOYMENT_TARGET on macOS.
platform = spack.platforms.by_name(pkg.spec.architecture.platform)
target = platform.target(pkg.spec.architecture.target)
platform.setup_platform_environment(pkg, env_mods)
tty.debug("setup_package: grabbing modifications from dependencies")
env_mods.extend(setup_context.get_env_modifications())
tty.debug("setup_package: collected all modifications from dependencies")
if context == Context.TEST:
env_mods.prepend_path("PATH", ".")
elif context == Context.BUILD and not dirty and not env_mods.is_unset("CPATH"):
@@ -1322,7 +1333,7 @@ def make_stack(tb, stack=None):
# don't provide context if the code is actually in the base classes.
obj = frame.f_locals["self"]
func = getattr(obj, tb.tb_frame.f_code.co_name, "")
if func:
if func and hasattr(func, "__qualname__"):
typename, *_ = func.__qualname__.partition(".")
if isinstance(obj, CONTEXT_BASES) and typename not in basenames:
break

View File

@@ -2,6 +2,8 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import warnings
import llnl.util.tty as tty
import llnl.util.tty.colify
import llnl.util.tty.color as cl
@@ -52,8 +54,10 @@ def setup_parser(subparser):
def configs(parser, args):
reports = spack.audit.run_group(args.subcommand)
_process_reports(reports)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
reports = spack.audit.run_group(args.subcommand)
_process_reports(reports)
def packages(parser, args):

View File

@@ -61,7 +61,7 @@ def graph(parser, args):
args.dot = True
env = ev.active_environment()
if env:
specs = env.all_specs()
specs = env.concrete_roots()
else:
specs = spack.store.STORE.db.query()

View File

@@ -327,7 +327,7 @@ def _variants_by_name_when(pkg):
"""Adaptor to get variants keyed by { name: { when: { [Variant...] } }."""
# TODO: replace with pkg.variants_by_name(when=True) when unified directive dicts are merged.
variants = {}
for name, (variant, whens) in pkg.variants.items():
for name, (variant, whens) in sorted(pkg.variants.items()):
for when in whens:
variants.setdefault(name, {}).setdefault(when, []).append(variant)
return variants

View File

@@ -154,6 +154,14 @@ def add_compilers_to_config(compilers, scope=None, init_config=True):
"""
compiler_config = get_compiler_config(scope, init_config)
for compiler in compilers:
if not compiler.cc:
tty.debug(f"{compiler.spec} does not have a C compiler")
if not compiler.cxx:
tty.debug(f"{compiler.spec} does not have a C++ compiler")
if not compiler.f77:
tty.debug(f"{compiler.spec} does not have a Fortran77 compiler")
if not compiler.fc:
tty.debug(f"{compiler.spec} does not have a Fortran compiler")
compiler_config.append(_to_dict(compiler))
spack.config.set("compilers", compiler_config, scope=scope)

View File

@@ -380,14 +380,13 @@ def _print_timer(pre: str, pkg_id: str, timer: timer.BaseTimer) -> None:
def _install_from_cache(
pkg: "spack.package_base.PackageBase", cache_only: bool, explicit: bool, unsigned: bool = False
pkg: "spack.package_base.PackageBase", explicit: bool, unsigned: bool = False
) -> bool:
"""
Extract the package from binary cache
Install the package from binary cache
Args:
pkg: package to install from the binary cache
cache_only: only extract from binary cache
explicit: ``True`` if installing the package was explicitly
requested by the user, otherwise, ``False``
unsigned: ``True`` if binary package signatures to be checked,
@@ -399,15 +398,11 @@ def _install_from_cache(
installed_from_cache = _try_install_from_binary_cache(
pkg, explicit, unsigned=unsigned, timer=t
)
pkg_id = package_id(pkg)
if not installed_from_cache:
pre = f"No binary for {pkg_id} found"
if cache_only:
tty.die(f"{pre} when cache-only specified")
tty.msg(f"{pre}: installing from source")
return False
t.stop()
pkg_id = package_id(pkg)
tty.debug(f"Successfully extracted {pkg_id} from binary cache")
_write_timer_json(pkg, t, True)
@@ -1335,7 +1330,6 @@ def _prepare_for_install(self, task: BuildTask) -> None:
"""
install_args = task.request.install_args
keep_prefix = install_args.get("keep_prefix")
restage = install_args.get("restage")
# Make sure the package is ready to be locally installed.
self._ensure_install_ready(task.pkg)
@@ -1367,10 +1361,6 @@ def _prepare_for_install(self, task: BuildTask) -> None:
else:
tty.debug(f"{task.pkg_id} is partially installed")
# Destroy the stage for a locally installed, non-DIYStage, package
if restage and task.pkg.stage.managed_by_spack:
task.pkg.stage.destroy()
if (
rec
and installed_in_db
@@ -1671,11 +1661,16 @@ def _install_task(self, task: BuildTask, install_status: InstallStatus) -> None:
task.status = STATUS_INSTALLING
# Use the binary cache if requested
if use_cache and _install_from_cache(pkg, cache_only, explicit, unsigned):
self._update_installed(task)
if task.compiler:
self._add_compiler_package_to_config(pkg)
return
if use_cache:
if _install_from_cache(pkg, explicit, unsigned):
self._update_installed(task)
if task.compiler:
self._add_compiler_package_to_config(pkg)
return
elif cache_only:
raise InstallError("No binary found when cache-only was specified", pkg=pkg)
else:
tty.msg(f"No binary for {pkg_id} found: installing from source")
pkg.run_tests = tests if isinstance(tests, bool) else pkg.name in tests
@@ -1691,6 +1686,10 @@ def _install_task(self, task: BuildTask, install_status: InstallStatus) -> None:
try:
self._setup_install_dir(pkg)
# Create stage object now and let it be serialized for the child process. That
# way monkeypatch in tests works correctly.
pkg.stage
# Create a child process to do the actual installation.
# Preserve verbosity settings across installs.
spack.package_base.PackageBase._verbose = spack.build_environment.start_build_process(
@@ -2223,11 +2222,6 @@ def install(self) -> None:
if not keep_prefix and not action == InstallAction.OVERWRITE:
pkg.remove_prefix()
# The subprocess *may* have removed the build stage. Mark it
# not created so that the next time pkg.stage is invoked, we
# check the filesystem for it.
pkg.stage.created = False
# Perform basic task cleanup for the installed spec to
# include downgrading the write to a read lock
self._cleanup_task(pkg)
@@ -2297,6 +2291,9 @@ def __init__(self, pkg: "spack.package_base.PackageBase", install_args: dict):
# whether to keep the build stage after installation
self.keep_stage = install_args.get("keep_stage", False)
# whether to restage
self.restage = install_args.get("restage", False)
# whether to skip the patch phase
self.skip_patch = install_args.get("skip_patch", False)
@@ -2327,9 +2324,13 @@ def __init__(self, pkg: "spack.package_base.PackageBase", install_args: dict):
def run(self) -> bool:
"""Main entry point from ``build_process`` to kick off install in child."""
self.pkg.stage.keep = self.keep_stage
stage = self.pkg.stage
stage.keep = self.keep_stage
with self.pkg.stage:
if self.restage:
stage.destroy()
with stage:
self.timer.start("stage")
if not self.fake:

View File

@@ -1016,14 +1016,16 @@ def _main(argv=None):
bootstrap_context = bootstrap.ensure_bootstrap_configuration()
with bootstrap_context:
return finish_parse_and_run(parser, cmd_name, args.command, env_format_error)
return finish_parse_and_run(parser, cmd_name, args, env_format_error)
def finish_parse_and_run(parser, cmd_name, cmd, env_format_error):
def finish_parse_and_run(parser, cmd_name, main_args, env_format_error):
"""Finish parsing after we know the command to run."""
# add the found command to the parser and re-run then re-parse
command = parser.add_command(cmd_name)
args, unknown = parser.parse_known_args()
args, unknown = parser.parse_known_args(main_args.command)
# we need to inherit verbose since the install command checks for it
args.verbose = main_args.verbose
# Now that we know what command this is and what its args are, determine
# whether we can continue with a bad environment and raise if not.

View File

@@ -93,7 +93,7 @@ def _filter_compiler_wrappers_impl(pkg_or_builder):
replacements = []
for idx, (env_var, compiler_path) in enumerate(compiler_vars):
if env_var in os.environ:
if env_var in os.environ and compiler_path is not None:
# filter spack wrapper and links to spack wrapper in case
# build system runs realpath
wrapper = os.environ[env_var]

View File

@@ -69,6 +69,8 @@
"patternProperties": {r"\w+": {}},
}
REQUIREMENT_URL = "https://spack.readthedocs.io/en/latest/packages_yaml.html#package-requirements"
#: Properties for inclusion in other schemas
properties = {
"packages": {
@@ -117,7 +119,7 @@
"properties": ["version"],
"message": "setting version preferences in the 'all' section of packages.yaml "
"is deprecated and will be removed in v0.22\n\n\tThese preferences "
"will be ignored by Spack. You can set them only in package specific sections "
"will be ignored by Spack. You can set them only in package-specific sections "
"of the same file.\n",
"error": False,
},
@@ -162,10 +164,14 @@
},
"deprecatedProperties": {
"properties": ["target", "compiler", "providers"],
"message": "setting compiler, target or provider preferences in a package "
"specific section of packages.yaml is deprecated, and will be removed in "
"v0.22.\n\n\tThese preferences will be ignored by Spack. You "
"can set them only in the 'all' section of the same file.\n",
"message": "setting 'compiler:', 'target:' or 'provider:' preferences in "
"a package-specific section of packages.yaml is deprecated, and will be "
"removed in v0.22.\n\n\tThese preferences will be ignored by Spack, and "
"can be set only in the 'all' section of the same file. "
"You can run:\n\n\t\t$ spack audit configs\n\n\tto get better diagnostics, "
"including files:lines where the deprecated attributes are used.\n\n"
"\tUse requirements to enforce conditions on specific packages: "
f"{REQUIREMENT_URL}\n",
"error": False,
},
}

View File

@@ -713,7 +713,7 @@ def _get_cause_tree(
(condition_id, set_id) in which the latter idea means that the condition represented by
the former held in the condition set represented by the latter.
"""
seen = set(seen) | set(cause)
seen.add(cause)
parents = [c for e, c in condition_causes if e == cause and c not in seen]
local = "required because %s " % conditions[cause[0]]
@@ -812,7 +812,14 @@ def on_model(model):
errors = sorted(
[(int(priority), msg, args) for priority, msg, *args in error_args], reverse=True
)
msg = self.message(errors)
try:
msg = self.message(errors)
except Exception as e:
msg = (
f"unexpected error during concretization [{str(e)}]. "
f"Please report a bug at https://github.com/spack/spack/issues"
)
raise spack.error.SpackError(msg)
raise UnsatisfiableSpecError(msg)
@@ -1006,14 +1013,6 @@ def on_model(model):
# record the possible dependencies in the solve
result.possible_dependencies = setup.pkgs
# print any unknown functions in the model
for sym in best_model:
if sym.name not in ("attr", "error", "opt_criterion"):
tty.debug(
"UNKNOWN SYMBOL: %s(%s)"
% (sym.name, ", ".join([str(s) for s in intermediate_repr(sym.arguments)]))
)
elif cores:
result.control = self.control
result.cores.extend(cores)
@@ -1118,11 +1117,8 @@ def __init__(self, tests=False):
self.reusable_and_possible = ConcreteSpecsByHash()
# id for dummy variables
self._condition_id_counter = itertools.count()
self._trigger_id_counter = itertools.count()
self._id_counter = itertools.count()
self._trigger_cache = collections.defaultdict(dict)
self._effect_id_counter = itertools.count()
self._effect_cache = collections.defaultdict(dict)
# Caches to optimize the setup phase of the solver
@@ -1518,7 +1514,7 @@ def condition(
# In this way, if a condition can't be emitted but the exception is handled in the caller,
# we won't emit partial facts.
condition_id = next(self._condition_id_counter)
condition_id = next(self._id_counter)
self.gen.fact(fn.pkg_fact(named_cond.name, fn.condition(condition_id)))
self.gen.fact(fn.condition_reason(condition_id, msg))
@@ -1526,7 +1522,7 @@ def condition(
named_cond_key = (str(named_cond), transform_required)
if named_cond_key not in cache:
trigger_id = next(self._trigger_id_counter)
trigger_id = next(self._id_counter)
requirements = self.spec_clauses(named_cond, body=True, required_from=name)
if transform_required:
@@ -1542,7 +1538,7 @@ def condition(
cache = self._effect_cache[named_cond.name]
imposed_spec_key = (str(imposed_spec), transform_imposed)
if imposed_spec_key not in cache:
effect_id = next(self._effect_id_counter)
effect_id = next(self._id_counter)
requirements = self.spec_clauses(imposed_spec, body=False, required_from=name)
if transform_imposed:
@@ -1832,7 +1828,13 @@ def preferred_variants(self, pkg_name):
# perform validation of the variant and values
spec = spack.spec.Spec(pkg_name)
spec.update_variant_validate(variant_name, values)
try:
spec.update_variant_validate(variant_name, values)
except (spack.variant.InvalidVariantValueError, KeyError, ValueError) as e:
tty.debug(
f"[SETUP]: rejected {str(variant)} as a preference for {pkg_name}: {str(e)}"
)
continue
for value in values:
self.variant_values_from_specs.add((pkg_name, variant.name, value))
@@ -2550,12 +2552,8 @@ def setup(
reuse: list of concrete specs that can be reused
allow_deprecated: if True adds deprecated versions into the solve
"""
self._condition_id_counter = itertools.count()
# preliminary checks
check_packages_exist(specs)
# get list of all possible dependencies
self.possible_virtuals = set(x.name for x in specs if x.virtual)
node_counter = _create_counter(specs, tests=self.tests)
@@ -2675,23 +2673,21 @@ def setup(
def literal_specs(self, specs):
for spec in specs:
self.gen.h2("Spec: %s" % str(spec))
condition_id = next(self._condition_id_counter)
trigger_id = next(self._trigger_id_counter)
condition_id = next(self._id_counter)
trigger_id = next(self._id_counter)
# Special condition triggered by "literal_solved"
self.gen.fact(fn.literal(trigger_id))
self.gen.fact(fn.pkg_fact(spec.name, fn.condition_trigger(condition_id, trigger_id)))
self.gen.fact(fn.condition_reason(condition_id, f"{spec} requested from CLI"))
self.gen.fact(fn.condition_reason(condition_id, f"{spec} requested explicitly"))
# Effect imposes the spec
imposed_spec_key = str(spec), None
cache = self._effect_cache[spec.name]
msg = (
"literal specs have different requirements. clear cache before computing literals"
)
assert imposed_spec_key not in cache, msg
effect_id = next(self._effect_id_counter)
requirements = self.spec_clauses(spec)
if imposed_spec_key in cache:
effect_id, requirements = cache[imposed_spec_key]
else:
effect_id = next(self._id_counter)
requirements = self.spec_clauses(spec)
root_name = spec.name
for clause in requirements:
clause_name = clause.args[0]
@@ -2792,9 +2788,11 @@ class SpecBuilder:
r"^.*_propagate$",
r"^.*_satisfies$",
r"^.*_set$",
r"^dependency_holds$",
r"^node_compiler$",
r"^package_hash$",
r"^root$",
r"^track_dependencies$",
r"^variant_default_value_from_cli$",
r"^virtual_node$",
r"^virtual_root$",

View File

@@ -213,6 +213,19 @@ def __call__(self, match):
return clr.colorize(re.sub(_SEPARATORS, insert_color(), str(spec)) + "@.")
OLD_STYLE_FMT_RE = re.compile(r"\${[A-Z]+}")
def ensure_modern_format_string(fmt: str) -> None:
"""Ensure that the format string does not contain old ${...} syntax."""
result = OLD_STYLE_FMT_RE.search(fmt)
if result:
raise SpecFormatStringError(
f"Format string `{fmt}` contains old syntax `{result.group(0)}`. "
"This is no longer supported."
)
@lang.lazy_lexicographic_ordering
class ArchSpec:
"""Aggregate the target platform, the operating system and the target microarchitecture."""
@@ -4360,6 +4373,7 @@ def format(self, format_string=DEFAULT_FORMAT, **kwargs):
that accepts a string and returns another one
"""
ensure_modern_format_string(format_string)
color = kwargs.get("color", False)
transform = kwargs.get("transform", {})

View File

@@ -201,6 +201,9 @@ def dummy_prefix(tmpdir):
with open(data, "w") as f:
f.write("hello world")
with open(p.join(".spack", "binary_distribution"), "w") as f:
f.write("{}")
os.symlink("app", relative_app_link)
os.symlink(app, absolute_app_link)
@@ -1024,7 +1027,9 @@ def test_tarball_common_prefix(dummy_prefix, tmpdir):
bindist._tar_strip_component(tar, common_prefix)
# Extract into prefix2
tar.extractall(path="prefix2")
tar.extractall(
path="prefix2", members=bindist._tar_strip_component(tar, common_prefix)
)
# Verify files are all there at the correct level.
assert set(os.listdir("prefix2")) == {"bin", "share", ".spack"}
@@ -1044,13 +1049,30 @@ def test_tarball_common_prefix(dummy_prefix, tmpdir):
)
def test_tarfile_missing_binary_distribution_file(tmpdir):
"""A tarfile that does not contain a .spack/binary_distribution file cannot be
used to install."""
with tmpdir.as_cwd():
# An empty .spack dir.
with tarfile.open("empty.tar", mode="w") as tar:
tarinfo = tarfile.TarInfo(name="example/.spack")
tarinfo.type = tarfile.DIRTYPE
tar.addfile(tarinfo)
with pytest.raises(ValueError, match="missing binary_distribution file"):
bindist._ensure_common_prefix(tarfile.open("empty.tar", mode="r"))
def test_tarfile_without_common_directory_prefix_fails(tmpdir):
"""A tarfile that only contains files without a common package directory
should fail to extract, as we won't know where to put the files."""
with tmpdir.as_cwd():
# Create a broken tarball with just a file, no directories.
with tarfile.open("empty.tar", mode="w") as tar:
tar.addfile(tarfile.TarInfo(name="example/file"), fileobj=io.BytesIO(b"hello"))
tar.addfile(
tarfile.TarInfo(name="example/.spack/binary_distribution"),
fileobj=io.BytesIO(b"hello"),
)
with pytest.raises(ValueError, match="Tarball does not contain a common prefix"):
bindist._ensure_common_prefix(tarfile.open("empty.tar", mode="r"))
@@ -1166,7 +1188,7 @@ def test_get_valid_spec_file_no_json(tmp_path, filename):
def test_download_tarball_with_unsupported_layout_fails(tmp_path, mutable_config, capsys):
layout_version = bindist.CURRENT_BUILD_CACHE_LAYOUT_VERSION + 1
layout_version = bindist.FORWARD_COMPAT_BUILD_CACHE_LAYOUT_VERSION + 1
spec = Spec("gmake@4.4.1%gcc@13.1.0 arch=linux-ubuntu23.04-zen2")
spec._mark_concrete()
spec_dict = spec.to_dict()

View File

@@ -25,7 +25,7 @@ def test_error_when_multiple_specs_are_given():
assert "only takes one spec" in output
@pytest.mark.parametrize("args", [("--", "/bin/bash", "-c", "echo test"), ("--",), ()])
@pytest.mark.parametrize("args", [("--", "/bin/sh", "-c", "echo test"), ("--",), ()])
@pytest.mark.usefixtures("config", "mock_packages", "working_env")
def test_build_env_requires_a_spec(args):
output = build_env(*args, fail_on_error=False)
@@ -35,7 +35,7 @@ def test_build_env_requires_a_spec(args):
_out_file = "env.out"
@pytest.mark.parametrize("shell", ["pwsh", "bat"] if sys.platform == "win32" else ["bash"])
@pytest.mark.parametrize("shell", ["pwsh", "bat"] if sys.platform == "win32" else ["sh"])
@pytest.mark.usefixtures("config", "mock_packages", "working_env")
def test_dump(shell_as, shell, tmpdir):
with tmpdir.as_cwd():

View File

@@ -2000,7 +2000,7 @@ def test_ci_reproduce(
install_script = os.path.join(working_dir.strpath, "install.sh")
with open(install_script, "w") as fd:
fd.write("#!/bin/bash\n\n#fake install\nspack install blah\n")
fd.write("#!/bin/sh\n\n#fake install\nspack install blah\n")
spack_info_file = os.path.join(working_dir.strpath, "spack_info.txt")
with open(spack_info_file, "w") as fd:

View File

@@ -3538,7 +3538,7 @@ def test_environment_created_in_users_location(mutable_mock_env_path, tmp_path):
assert os.path.isdir(os.path.join(env_dir, dir_name))
def test_environment_created_from_lockfile_has_view(mock_packages, tmpdir):
def test_environment_created_from_lockfile_has_view(mock_packages, temporary_store, tmpdir):
"""When an env is created from a lockfile, a view should be generated for it"""
env_a = str(tmpdir.join("a"))
env_b = str(tmpdir.join("b"))

View File

@@ -43,7 +43,7 @@ def test_find_gpg(cmd_name, version, tmpdir, mock_gnupghome, monkeypatch):
f.write(TEMPLATE.format(version=version))
fs.set_executable(fname)
monkeypatch.setitem(os.environ, "PATH", str(tmpdir))
monkeypatch.setenv("PATH", str(tmpdir))
if version == "undetectable" or version.endswith("1.3.4"):
with pytest.raises(spack.util.gpg.SpackGPGError):
spack.util.gpg.init(force=True)
@@ -54,7 +54,7 @@ def test_find_gpg(cmd_name, version, tmpdir, mock_gnupghome, monkeypatch):
def test_no_gpg_in_path(tmpdir, mock_gnupghome, monkeypatch, mutable_config):
monkeypatch.setitem(os.environ, "PATH", str(tmpdir))
monkeypatch.setenv("PATH", str(tmpdir))
bootstrap("disable")
with pytest.raises(RuntimeError):
spack.util.gpg.init(force=True)

View File

@@ -904,13 +904,12 @@ def test_install_help_cdash():
@pytest.mark.disable_clean_stage_check
def test_cdash_auth_token(tmpdir, mock_fetch, install_mockery, capfd):
def test_cdash_auth_token(tmpdir, mock_fetch, install_mockery, monkeypatch, capfd):
# capfd interferes with Spack's capturing
with tmpdir.as_cwd():
with capfd.disabled():
os.environ["SPACK_CDASH_AUTH_TOKEN"] = "asdf"
out = install("-v", "--log-file=cdash_reports", "--log-format=cdash", "a")
assert "Using CDash auth token from environment" in out
with tmpdir.as_cwd(), capfd.disabled():
monkeypatch.setenv("SPACK_CDASH_AUTH_TOKEN", "asdf")
out = install("-v", "--log-file=cdash_reports", "--log-format=cdash", "a")
assert "Using CDash auth token from environment" in out
@pytest.mark.not_on_windows("Windows log_output logs phase header out of order")

View File

@@ -253,8 +253,8 @@ def test_get_compiler_link_paths_load_env(working_env, monkeypatch, tmpdir):
gcc = str(tmpdir.join("gcc"))
with open(gcc, "w") as f:
f.write(
"""#!/bin/bash
if [[ $ENV_SET == "1" && $MODULE_LOADED == "1" ]]; then
"""#!/bin/sh
if [ "$ENV_SET" = "1" ] && [ "$MODULE_LOADED" = "1" ]; then
echo '"""
+ no_flag_output
+ """'
@@ -701,8 +701,8 @@ def test_compiler_get_real_version(working_env, monkeypatch, tmpdir):
gcc = str(tmpdir.join("gcc"))
with open(gcc, "w") as f:
f.write(
"""#!/bin/bash
if [[ $CMP_ON == "1" ]]; then
"""#!/bin/sh
if [ "$CMP_ON" = "1" ]; then
echo "$CMP_VER"
fi
"""
@@ -747,8 +747,8 @@ def test_compiler_get_real_version_fails(working_env, monkeypatch, tmpdir):
gcc = str(tmpdir.join("gcc"))
with open(gcc, "w") as f:
f.write(
"""#!/bin/bash
if [[ $CMP_ON == "1" ]]; then
"""#!/bin/sh
if [ "$CMP_ON" = "1" ]; then
echo "$CMP_VER"
fi
"""
@@ -801,7 +801,7 @@ def test_compiler_flags_use_real_version(working_env, monkeypatch, tmpdir):
gcc = str(tmpdir.join("gcc"))
with open(gcc, "w") as f:
f.write(
"""#!/bin/bash
"""#!/bin/sh
echo "4.4.4"
"""
) # Version for which c++11 flag is -std=c++0x

View File

@@ -203,7 +203,9 @@ def change(self, changes=None):
# TODO: in case tests using this fixture start failing.
if sys.modules.get("spack.pkg.changing.changing"):
del sys.modules["spack.pkg.changing.changing"]
if sys.modules.get("spack.pkg.changing.root"):
del sys.modules["spack.pkg.changing.root"]
if sys.modules.get("spack.pkg.changing"):
del sys.modules["spack.pkg.changing"]
# Change the recipe
@@ -1604,7 +1606,9 @@ def test_installed_version_is_selected_only_for_reuse(
assert not new_root["changing"].satisfies("@1.0")
@pytest.mark.regression("28259")
def test_reuse_with_unknown_namespace_dont_raise(self, mock_custom_repository):
def test_reuse_with_unknown_namespace_dont_raise(
self, temporary_store, mock_custom_repository
):
with spack.repo.use_repositories(mock_custom_repository, override=False):
s = Spec("c").concretized()
assert s.namespace != "builtin.mock"
@@ -1615,8 +1619,8 @@ def test_reuse_with_unknown_namespace_dont_raise(self, mock_custom_repository):
assert s.namespace == "builtin.mock"
@pytest.mark.regression("28259")
def test_reuse_with_unknown_package_dont_raise(self, tmpdir, monkeypatch):
builder = spack.repo.MockRepositoryBuilder(tmpdir, namespace="myrepo")
def test_reuse_with_unknown_package_dont_raise(self, tmpdir, temporary_store, monkeypatch):
builder = spack.repo.MockRepositoryBuilder(tmpdir.mkdir("mock.repo"), namespace="myrepo")
builder.add_package("c")
with spack.repo.use_repositories(builder.root, override=False):
s = Spec("c").concretized()

View File

@@ -16,8 +16,8 @@
version_error_messages = [
"Cannot satisfy 'fftw@:1.0' and 'fftw@1.1:",
" required because quantum-espresso depends on fftw@:1.0",
" required because quantum-espresso ^fftw@1.1: requested from CLI",
" required because quantum-espresso ^fftw@1.1: requested from CLI",
" required because quantum-espresso ^fftw@1.1: requested explicitly",
" required because quantum-espresso ^fftw@1.1: requested explicitly",
]
external_error_messages = [
@@ -30,15 +30,15 @@
" which was not satisfied"
),
" 'quantum-espresso+veritas' required",
" required because quantum-espresso+veritas requested from CLI",
" required because quantum-espresso+veritas requested explicitly",
]
variant_error_messages = [
"'fftw' required multiple values for single-valued variant 'mpi'",
" Requested '~mpi' and '+mpi'",
" required because quantum-espresso depends on fftw+mpi when +invino",
" required because quantum-espresso+invino ^fftw~mpi requested from CLI",
" required because quantum-espresso+invino ^fftw~mpi requested from CLI",
" required because quantum-espresso+invino ^fftw~mpi requested explicitly",
" required because quantum-espresso+invino ^fftw~mpi requested explicitly",
]
external_config = {

View File

@@ -504,3 +504,13 @@ def test_sticky_variant_accounts_for_packages_yaml(self):
with spack.config.override("packages:sticky-variant", {"variants": "+allow-gcc"}):
s = Spec("sticky-variant %gcc").concretized()
assert s.satisfies("%gcc") and s.satisfies("+allow-gcc")
@pytest.mark.regression("41134")
@pytest.mark.only_clingo("Not backporting the fix to the old concretizer")
def test_default_preference_variant_different_type_does_not_error(self):
"""Tests that a different type for an existing variant in the 'all:' section of
packages.yaml doesn't fail with an error.
"""
with spack.config.override("packages:all", {"variants": "+foo"}):
s = Spec("a").concretized()
assert s.satisfies("foo=bar")

View File

@@ -1239,11 +1239,11 @@ def test_user_config_path_is_default_when_env_var_is_empty(working_env):
assert os.path.expanduser("~%s.spack" % os.sep) == spack.paths._get_user_config_path()
def test_default_install_tree(monkeypatch):
def test_default_install_tree(monkeypatch, default_config):
s = spack.spec.Spec("nonexistent@x.y.z %none@a.b.c arch=foo-bar-baz")
monkeypatch.setattr(s, "dag_hash", lambda: "abc123")
projection = spack.config.get("config:install_tree:projections:all", scope="defaults")
assert s.format(projection) == "foo-bar-baz/none-a.b.c/nonexistent-x.y.z-abc123"
_, _, projections = spack.store.parse_install_tree(spack.config.get("config"))
assert s.format(projections["all"]) == "foo-bar-baz/none-a.b.c/nonexistent-x.y.z-abc123"
def test_local_config_can_be_disabled(working_env):

View File

@@ -629,7 +629,7 @@ def platform_config():
spack.config.add_default_platform_scope(spack.platforms.real_host().name)
@pytest.fixture(scope="session")
@pytest.fixture
def default_config():
"""Isolates the default configuration from the user configs.
@@ -713,9 +713,6 @@ def configuration_dir(tmpdir_factory, linux_os):
t.write(content)
yield tmpdir
# Once done, cleanup the directory
shutil.rmtree(str(tmpdir))
def _create_mock_configuration_scopes(configuration_dir):
"""Create the configuration scopes used in `config` and `mutable_config`."""

View File

@@ -695,7 +695,7 @@ def test_removing_spec_from_manifest_with_exact_duplicates(
@pytest.mark.regression("35298")
@pytest.mark.only_clingo("Propagation not supported in the original concretizer")
def test_variant_propagation_with_unify_false(tmp_path, mock_packages):
def test_variant_propagation_with_unify_false(tmp_path, mock_packages, config):
"""Spack distributes concretizations to different processes, when unify:false is selected and
the number of roots is 2 or more. When that happens, the specs to be concretized need to be
properly reconstructed on the worker process, if variant propagation was requested.
@@ -778,3 +778,32 @@ def test_env_with_include_def_missing(mutable_mock_env_path, mock_packages):
with e:
with pytest.raises(UndefinedReferenceError, match=r"which does not appear"):
e.concretize()
@pytest.mark.regression("41292")
def test_deconcretize_then_concretize_does_not_error(mutable_mock_env_path, mock_packages):
"""Tests that, after having deconcretized a spec, we can reconcretize an environment which
has 2 or more user specs mapping to the same concrete spec.
"""
mutable_mock_env_path.mkdir()
spack_yaml = mutable_mock_env_path / ev.manifest_name
spack_yaml.write_text(
"""spack:
specs:
# These two specs concretize to the same hash
- c
- c@1.0
# Spec used to trigger the bug
- a
concretizer:
unify: true
"""
)
e = ev.Environment(mutable_mock_env_path)
with e:
e.concretize()
e.deconcretize(spack.spec.Spec("a"), concrete=False)
e.concretize()
assert len(e.concrete_roots()) == 3
all_root_hashes = set(x.dag_hash() for x in e.concrete_roots())
assert len(all_root_hashes) == 2

View File

@@ -12,10 +12,12 @@
import llnl.util.filesystem as fs
import spack.error
import spack.mirror
import spack.patch
import spack.repo
import spack.store
import spack.util.spack_json as sjson
from spack import binary_distribution
from spack.package_base import (
InstallError,
PackageBase,
@@ -118,59 +120,25 @@ def remove_prefix(self):
self.wrapped_rm_prefix()
class MockStage:
def __init__(self, wrapped_stage):
self.wrapped_stage = wrapped_stage
self.test_destroyed = False
def __enter__(self):
self.create()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
self.destroy()
def destroy(self):
self.test_destroyed = True
self.wrapped_stage.destroy()
def create(self):
self.wrapped_stage.create()
def __getattr__(self, attr):
if attr == "wrapped_stage":
# This attribute may not be defined at some point during unpickling
raise AttributeError()
return getattr(self.wrapped_stage, attr)
def test_partial_install_delete_prefix_and_stage(install_mockery, mock_fetch, working_env):
s = Spec("canfail").concretized()
instance_rm_prefix = s.package.remove_prefix
try:
s.package.remove_prefix = mock_remove_prefix
with pytest.raises(MockInstallError):
s.package.do_install()
assert os.path.isdir(s.package.prefix)
rm_prefix_checker = RemovePrefixChecker(instance_rm_prefix)
s.package.remove_prefix = rm_prefix_checker.remove_prefix
s.package.remove_prefix = mock_remove_prefix
with pytest.raises(MockInstallError):
s.package.do_install()
assert os.path.isdir(s.package.prefix)
rm_prefix_checker = RemovePrefixChecker(instance_rm_prefix)
s.package.remove_prefix = rm_prefix_checker.remove_prefix
# must clear failure markings for the package before re-installing it
spack.store.STORE.failure_tracker.clear(s, True)
# must clear failure markings for the package before re-installing it
spack.store.STORE.failure_tracker.clear(s, True)
s.package.set_install_succeed()
s.package.stage = MockStage(s.package.stage)
s.package.do_install(restage=True)
assert rm_prefix_checker.removed
assert s.package.stage.test_destroyed
assert s.package.spec.installed
finally:
s.package.remove_prefix = instance_rm_prefix
s.package.set_install_succeed()
s.package.do_install(restage=True)
assert rm_prefix_checker.removed
assert s.package.spec.installed
@pytest.mark.disable_clean_stage_check
@@ -357,10 +325,8 @@ def test_partial_install_keep_prefix(install_mockery, mock_fetch, monkeypatch, w
spack.store.STORE.failure_tracker.clear(s, True)
s.package.set_install_succeed()
s.package.stage = MockStage(s.package.stage)
s.package.do_install(keep_prefix=True)
assert s.package.spec.installed
assert not s.package.stage.test_destroyed
def test_second_install_no_overwrite_first(install_mockery, mock_fetch, monkeypatch):
@@ -644,3 +610,48 @@ def test_empty_install_sanity_check_prefix(
spec = Spec("failing-empty-install").concretized()
with pytest.raises(spack.build_environment.ChildError, match="Nothing was installed"):
spec.package.do_install()
def test_install_from_binary_with_missing_patch_succeeds(
temporary_store: spack.store.Store, mutable_config, tmp_path, mock_packages
):
"""If a patch is missing in the local package repository, but was present when building and
pushing the package to a binary cache, installation from that binary cache shouldn't error out
because of the missing patch."""
# Create a spec s with non-existing patches
s = Spec("trivial-install-test-package").concretized()
patches = ["a" * 64]
s_dict = s.to_dict()
s_dict["spec"]["nodes"][0]["patches"] = patches
s_dict["spec"]["nodes"][0]["parameters"]["patches"] = patches
s = Spec.from_dict(s_dict)
# Create an install dir for it
os.makedirs(os.path.join(s.prefix, ".spack"))
with open(os.path.join(s.prefix, ".spack", "spec.json"), "w") as f:
s.to_json(f)
# And register it in the database
temporary_store.db.add(s, directory_layout=temporary_store.layout, explicit=True)
# Push it to a binary cache
build_cache = tmp_path / "my_build_cache"
binary_distribution.push_or_raise(
s,
build_cache.as_uri(),
binary_distribution.PushOptions(unsigned=True, regenerate_index=True),
)
# Now re-install it.
s.package.do_uninstall()
assert not temporary_store.db.query_local_by_spec_hash(s.dag_hash())
# Source install: fails, we don't have the patch.
with pytest.raises(spack.error.SpecError, match="Couldn't find patch for package"):
s.package.do_install()
# Binary install: succeeds, we don't need the patch.
spack.mirror.add(spack.mirror.Mirror.from_local_path(str(build_cache)))
s.package.do_install(package_cache_only=True, dependencies_cache_only=True, unsigned=True)
assert temporary_store.db.query_local_by_spec_hash(s.dag_hash())

View File

@@ -165,23 +165,19 @@ def test_install_msg(monkeypatch):
assert inst.install_msg(name, pid, None) == expected
def test_install_from_cache_errors(install_mockery, capsys):
"""Test to ensure cover _install_from_cache errors."""
def test_install_from_cache_errors(install_mockery):
"""Test to ensure cover install from cache errors."""
spec = spack.spec.Spec("trivial-install-test-package")
spec.concretize()
assert spec.concrete
# Check with cache-only
with pytest.raises(SystemExit):
inst._install_from_cache(spec.package, True, True, False)
captured = str(capsys.readouterr())
assert "No binary" in captured
assert "found when cache-only specified" in captured
with pytest.raises(inst.InstallError, match="No binary found when cache-only was specified"):
spec.package.do_install(package_cache_only=True, dependencies_cache_only=True)
assert not spec.package.installed_from_binary_cache
# Check when don't expect to install only from binary cache
assert not inst._install_from_cache(spec.package, False, True, False)
assert not inst._install_from_cache(spec.package, explicit=True, unsigned=False)
assert not spec.package.installed_from_binary_cache
@@ -192,7 +188,7 @@ def test_install_from_cache_ok(install_mockery, monkeypatch):
monkeypatch.setattr(inst, "_try_install_from_binary_cache", _true)
monkeypatch.setattr(spack.hooks, "post_install", _noop)
assert inst._install_from_cache(spec.package, True, True, False)
assert inst._install_from_cache(spec.package, explicit=True, unsigned=False)
def test_process_external_package_module(install_mockery, monkeypatch, capfd):

View File

@@ -9,10 +9,7 @@
This just tests whether the right args are getting passed to make.
"""
import os
import shutil
import sys
import tempfile
import unittest
import pytest
@@ -20,110 +17,104 @@
from spack.util.environment import path_put_first
pytestmark = pytest.mark.skipif(
sys.platform == "win32",
reason="MakeExecutable \
not supported on Windows",
sys.platform == "win32", reason="MakeExecutable not supported on Windows"
)
class MakeExecutableTest(unittest.TestCase):
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
@pytest.fixture(autouse=True)
def make_executable(tmp_path, working_env):
make_exe = tmp_path / "make"
with open(make_exe, "w") as f:
f.write("#!/bin/sh\n")
f.write('echo "$@"')
os.chmod(make_exe, 0o700)
make_exe = os.path.join(self.tmpdir, "make")
with open(make_exe, "w") as f:
f.write("#!/bin/sh\n")
f.write('echo "$@"')
os.chmod(make_exe, 0o700)
path_put_first("PATH", [tmp_path])
path_put_first("PATH", [self.tmpdir])
def tearDown(self):
shutil.rmtree(self.tmpdir)
def test_make_normal():
make = MakeExecutable("make", 8)
assert make(output=str).strip() == "-j8"
assert make("install", output=str).strip() == "-j8 install"
def test_make_normal(self):
make = MakeExecutable("make", 8)
self.assertEqual(make(output=str).strip(), "-j8")
self.assertEqual(make("install", output=str).strip(), "-j8 install")
def test_make_explicit(self):
make = MakeExecutable("make", 8)
self.assertEqual(make(parallel=True, output=str).strip(), "-j8")
self.assertEqual(make("install", parallel=True, output=str).strip(), "-j8 install")
def test_make_explicit():
make = MakeExecutable("make", 8)
assert make(parallel=True, output=str).strip() == "-j8"
assert make("install", parallel=True, output=str).strip() == "-j8 install"
def test_make_one_job(self):
make = MakeExecutable("make", 1)
self.assertEqual(make(output=str).strip(), "-j1")
self.assertEqual(make("install", output=str).strip(), "-j1 install")
def test_make_parallel_false(self):
make = MakeExecutable("make", 8)
self.assertEqual(make(parallel=False, output=str).strip(), "-j1")
self.assertEqual(make("install", parallel=False, output=str).strip(), "-j1 install")
def test_make_one_job():
make = MakeExecutable("make", 1)
assert make(output=str).strip() == "-j1"
assert make("install", output=str).strip() == "-j1 install"
def test_make_parallel_disabled(self):
make = MakeExecutable("make", 8)
os.environ["SPACK_NO_PARALLEL_MAKE"] = "true"
self.assertEqual(make(output=str).strip(), "-j1")
self.assertEqual(make("install", output=str).strip(), "-j1 install")
def test_make_parallel_false():
make = MakeExecutable("make", 8)
assert make(parallel=False, output=str).strip() == "-j1"
assert make("install", parallel=False, output=str).strip() == "-j1 install"
os.environ["SPACK_NO_PARALLEL_MAKE"] = "1"
self.assertEqual(make(output=str).strip(), "-j1")
self.assertEqual(make("install", output=str).strip(), "-j1 install")
# These don't disable (false and random string)
os.environ["SPACK_NO_PARALLEL_MAKE"] = "false"
self.assertEqual(make(output=str).strip(), "-j8")
self.assertEqual(make("install", output=str).strip(), "-j8 install")
def test_make_parallel_disabled(monkeypatch):
make = MakeExecutable("make", 8)
os.environ["SPACK_NO_PARALLEL_MAKE"] = "foobar"
self.assertEqual(make(output=str).strip(), "-j8")
self.assertEqual(make("install", output=str).strip(), "-j8 install")
monkeypatch.setenv("SPACK_NO_PARALLEL_MAKE", "true")
assert make(output=str).strip() == "-j1"
assert make("install", output=str).strip() == "-j1 install"
del os.environ["SPACK_NO_PARALLEL_MAKE"]
monkeypatch.setenv("SPACK_NO_PARALLEL_MAKE", "1")
assert make(output=str).strip() == "-j1"
assert make("install", output=str).strip() == "-j1 install"
def test_make_parallel_precedence(self):
make = MakeExecutable("make", 8)
# These don't disable (false and random string)
monkeypatch.setenv("SPACK_NO_PARALLEL_MAKE", "false")
assert make(output=str).strip() == "-j8"
assert make("install", output=str).strip() == "-j8 install"
# These should work
os.environ["SPACK_NO_PARALLEL_MAKE"] = "true"
self.assertEqual(make(parallel=True, output=str).strip(), "-j1")
self.assertEqual(make("install", parallel=True, output=str).strip(), "-j1 install")
monkeypatch.setenv("SPACK_NO_PARALLEL_MAKE", "foobar")
assert make(output=str).strip() == "-j8"
assert make("install", output=str).strip() == "-j8 install"
os.environ["SPACK_NO_PARALLEL_MAKE"] = "1"
self.assertEqual(make(parallel=True, output=str).strip(), "-j1")
self.assertEqual(make("install", parallel=True, output=str).strip(), "-j1 install")
# These don't disable (false and random string)
os.environ["SPACK_NO_PARALLEL_MAKE"] = "false"
self.assertEqual(make(parallel=True, output=str).strip(), "-j8")
self.assertEqual(make("install", parallel=True, output=str).strip(), "-j8 install")
def test_make_parallel_precedence(monkeypatch):
make = MakeExecutable("make", 8)
os.environ["SPACK_NO_PARALLEL_MAKE"] = "foobar"
self.assertEqual(make(parallel=True, output=str).strip(), "-j8")
self.assertEqual(make("install", parallel=True, output=str).strip(), "-j8 install")
# These should work
monkeypatch.setenv("SPACK_NO_PARALLEL_MAKE", "true")
assert make(parallel=True, output=str).strip() == "-j1"
assert make("install", parallel=True, output=str).strip() == "-j1 install"
del os.environ["SPACK_NO_PARALLEL_MAKE"]
monkeypatch.setenv("SPACK_NO_PARALLEL_MAKE", "1")
assert make(parallel=True, output=str).strip() == "-j1"
assert make("install", parallel=True, output=str).strip() == "-j1 install"
def test_make_jobs_env(self):
make = MakeExecutable("make", 8)
dump_env = {}
self.assertEqual(
make(output=str, jobs_env="MAKE_PARALLELISM", _dump_env=dump_env).strip(), "-j8"
)
self.assertEqual(dump_env["MAKE_PARALLELISM"], "8")
# These don't disable (false and random string)
monkeypatch.setenv("SPACK_NO_PARALLEL_MAKE", "false")
assert make(parallel=True, output=str).strip() == "-j8"
assert make("install", parallel=True, output=str).strip() == "-j8 install"
def test_make_jobserver(self):
make = MakeExecutable("make", 8)
os.environ["MAKEFLAGS"] = "--jobserver-auth=X,Y"
self.assertEqual(make(output=str).strip(), "")
self.assertEqual(make(parallel=False, output=str).strip(), "-j1")
del os.environ["MAKEFLAGS"]
monkeypatch.setenv("SPACK_NO_PARALLEL_MAKE", "foobar")
assert make(parallel=True, output=str).strip() == "-j8"
assert make("install", parallel=True, output=str).strip() == "-j8 install"
def test_make_jobserver_not_supported(self):
make = MakeExecutable("make", 8, supports_jobserver=False)
os.environ["MAKEFLAGS"] = "--jobserver-auth=X,Y"
# Currently fallback on default job count, Maybe it should force -j1 ?
self.assertEqual(make(output=str).strip(), "-j8")
del os.environ["MAKEFLAGS"]
def test_make_jobs_env():
make = MakeExecutable("make", 8)
dump_env = {}
assert make(output=str, jobs_env="MAKE_PARALLELISM", _dump_env=dump_env).strip() == "-j8"
assert dump_env["MAKE_PARALLELISM"] == "8"
def test_make_jobserver(monkeypatch):
make = MakeExecutable("make", 8)
monkeypatch.setenv("MAKEFLAGS", "--jobserver-auth=X,Y")
assert make(output=str).strip() == ""
assert make(parallel=False, output=str).strip() == "-j1"
def test_make_jobserver_not_supported(monkeypatch):
make = MakeExecutable("make", 8, supports_jobserver=False)
monkeypatch.setenv("MAKEFLAGS", "--jobserver-auth=X,Y")
# Currently fallback on default job count, Maybe it should force -j1 ?
assert make(output=str).strip() == "-j8"

View File

@@ -27,16 +27,13 @@
]
def test_module_function_change_env(tmpdir, working_env):
src_file = str(tmpdir.join("src_me"))
with open(src_file, "w") as f:
f.write("export TEST_MODULE_ENV_VAR=TEST_SUCCESS\n")
os.environ["NOT_AFFECTED"] = "NOT_AFFECTED"
module("load", src_file, module_template=". {0} 2>&1".format(src_file))
assert os.environ["TEST_MODULE_ENV_VAR"] == "TEST_SUCCESS"
assert os.environ["NOT_AFFECTED"] == "NOT_AFFECTED"
def test_module_function_change_env(tmp_path):
environb = {b"TEST_MODULE_ENV_VAR": b"TEST_FAIL", b"NOT_AFFECTED": b"NOT_AFFECTED"}
src_file = tmp_path / "src_me"
src_file.write_text("export TEST_MODULE_ENV_VAR=TEST_SUCCESS\n")
module("load", str(src_file), module_template=f". {src_file} 2>&1", environb=environb)
assert environb[b"TEST_MODULE_ENV_VAR"] == b"TEST_SUCCESS"
assert environb[b"NOT_AFFECTED"] == b"NOT_AFFECTED"
def test_module_function_no_change(tmpdir):

View File

@@ -1517,3 +1517,9 @@ def test_edge_equality_does_not_depend_on_virtual_order():
assert edge1 == edge2
assert tuple(sorted(edge1.virtuals)) == edge1.virtuals
assert tuple(sorted(edge2.virtuals)) == edge1.virtuals
def test_old_format_strings_trigger_error(default_mock_concretization):
s = Spec("a").concretized()
with pytest.raises(SpecFormatStringError):
s.format("${PACKAGE}-${VERSION}-${HASH}")

View File

@@ -147,7 +147,8 @@ def test_reverse_environment_modifications(working_env):
reversal = to_reverse.reversed()
os.environ = start_env.copy()
os.environ.clear()
os.environ.update(start_env)
print(os.environ)
to_reverse.apply_modifications()

View File

@@ -89,8 +89,8 @@ def test_which_with_slash_ignores_path(tmpdir, working_env):
assert exe.path == path
def test_which(tmpdir):
os.environ["PATH"] = str(tmpdir)
def test_which(tmpdir, monkeypatch):
monkeypatch.setenv("PATH", str(tmpdir))
assert ex.which("spack-test-exe") is None
with pytest.raises(ex.CommandNotFoundError):

View File

@@ -10,6 +10,7 @@
import os
import re
import subprocess
from typing import MutableMapping, Optional
import llnl.util.tty as tty
@@ -21,8 +22,13 @@
awk_cmd = r"""awk 'BEGIN{for(name in ENVIRON)""" r"""printf("%s=%s%c", name, ENVIRON[name], 0)}'"""
def module(*args, **kwargs):
module_cmd = kwargs.get("module_template", "module " + " ".join(args))
def module(
*args,
module_template: Optional[str] = None,
environb: Optional[MutableMapping[bytes, bytes]] = None,
):
module_cmd = module_template or ("module " + " ".join(args))
environb = environb or os.environb
if args[0] in module_change_commands:
# Suppress module output
@@ -33,10 +39,10 @@ def module(*args, **kwargs):
stderr=subprocess.STDOUT,
shell=True,
executable="/bin/bash",
env=environb,
)
# In Python 3, keys and values of `environ` are byte strings.
environ = {}
new_environb = {}
output = module_p.communicate()[0]
# Loop over each environment variable key=value byte string
@@ -45,11 +51,11 @@ def module(*args, **kwargs):
parts = entry.split(b"=", 1)
if len(parts) != 2:
continue
environ[parts[0]] = parts[1]
new_environb[parts[0]] = parts[1]
# Update os.environ with new dict
os.environ.clear()
os.environb.update(environ) # novermin
environb.clear()
environb.update(new_environb) # novermin
else:
# Simply execute commands that don't change state and return output

View File

@@ -370,25 +370,25 @@ e4s-rocm-external-build:
########################################
# GPU Testing Pipeline
########################################
.gpu-tests:
extends: [ ".linux_x86_64_v3" ]
variables:
SPACK_CI_STACK_NAME: gpu-tests
# .gpu-tests:
# extends: [ ".linux_x86_64_v3" ]
# variables:
# SPACK_CI_STACK_NAME: gpu-tests
gpu-tests-generate:
extends: [ ".gpu-tests", ".generate-x86_64"]
image: ghcr.io/spack/ubuntu20.04-runner-x86_64:2023-01-01
# gpu-tests-generate:
# extends: [ ".gpu-tests", ".generate-x86_64"]
# image: ghcr.io/spack/ubuntu20.04-runner-x86_64:2023-01-01
gpu-tests-build:
extends: [ ".gpu-tests", ".build" ]
trigger:
include:
- artifact: jobs_scratch_dir/cloud-ci-pipeline.yml
job: gpu-tests-generate
strategy: depend
needs:
- artifacts: True
job: gpu-tests-generate
# gpu-tests-build:
# extends: [ ".gpu-tests", ".build" ]
# trigger:
# include:
# - artifact: jobs_scratch_dir/cloud-ci-pipeline.yml
# job: gpu-tests-generate
# strategy: depend
# needs:
# - artifacts: True
# job: gpu-tests-generate
########################################
# E4S OneAPI Pipeline

View File

@@ -16,22 +16,28 @@ class Berkeleygw(MakefilePackage):
maintainers("migueldiascosta")
version(
"3.1.0",
sha256="7e890a5faa5a6bb601aa665c73903b3af30df7bdd13ee09362b69793bbefa6d2",
url="https://app.box.com/shared/static/2bik75lrs85zt281ydbup2xa7i5594gy.gz",
expand=False,
)
version(
"3.0.1",
sha256="7d8c2cc1ee679afb48efbdd676689d4d537226b50e13a049dbcb052aaaf3654f",
url="https://berkeley.box.com/shared/static/m1dgnhiemo47lhxczrn6si71bwxoxor8.gz",
url="https://app.box.com/shared/static/m1dgnhiemo47lhxczrn6si71bwxoxor8.gz",
expand=False,
)
version(
"3.0",
sha256="ab411acead5e979fd42b8d298dbb0a12ce152e7be9eee0bb87e9e5a06a638e2a",
url="https://berkeley.box.com/shared/static/lp6hj4kxr459l5a6t05qfuzl2ucyo03q.gz",
url="https://app.box.com/shared/static/lp6hj4kxr459l5a6t05qfuzl2ucyo03q.gz",
expand=False,
)
version(
"2.1",
sha256="31f3b643dd937350c3866338321d675d4a1b1f54c730b43ad74ae67e75a9e6f2",
url="https://berkeley.box.com/shared/static/ze3azi5vlyw7hpwvl9i5f82kaiid6g0x.gz",
url="https://app.box.com/shared/static/ze3azi5vlyw7hpwvl9i5f82kaiid6g0x.gz",
expand=False,
)

View File

@@ -122,8 +122,9 @@ def pgo_train(self):
# Run spack solve --fresh hdf5 with instrumented clingo.
python_runtime_env = EnvironmentModifications()
for s in self.spec.traverse(deptype=("run", "link"), order="post"):
python_runtime_env.extend(spack.user_environment.environment_modifications_for_spec(s))
python_runtime_env.extend(
spack.user_environment.environment_modifications_for_specs(self.spec)
)
python_runtime_env.unset("SPACK_ENV")
python_runtime_env.unset("SPACK_PYTHON")
self.spec["python"].command(

View File

@@ -18,7 +18,7 @@ class Cpr(CMakePackage):
version("1.9.2", sha256="3bfbffb22c51f322780d10d3ca8f79424190d7ac4b5ad6ad896de08dbd06bf31")
depends_on("curl")
depends_on("git", when="build")
depends_on("git", type="build")
def cmake_args(self):
_force = "_FORCE" if self.spec.satisfies("@:1.9") else ""

View File

@@ -45,21 +45,21 @@ class Interproscan(Package):
)
resource(
when="5.56-89.0 +databases",
when="@5.56-89.0 +databases",
name="databases",
url="https://ftp.ebi.ac.uk/pub/databases/interpro/iprscan/5/5.56-89.0/alt/interproscan-data-5.56-89.0.tar.gz",
sha256="49cd0c69711f9469f3b68857f4581b23ff12765ca2b12893d18e5a9a5cd8032d",
)
resource(
when="5.38-76.0 +databases",
when="@5.38-76.0 +databases",
name="databases",
url="https://ftp.ebi.ac.uk/pub/databases/interpro/iprscan/5/5.38-76.0/alt/interproscan-data-5.38-76.0.tar.gz",
sha256="e05e15d701037504f92ecf849c20317e70df28e78ff1945826b3c1e16d9b9cce",
)
resource(
when="5.36-75.0 +databases",
when="@5.36-75.0 +databases",
name="databases",
url="https://ftp.ebi.ac.uk/pub/databases/interpro/iprscan/5/5.36-75.0/alt/interproscan-data-5.36-75.0.tar.gz",
sha256="e9b1e6f2d1c20d06661a31a08c973bc8ddf039a4cf1e45ec4443200375e5d6a4",

View File

@@ -314,7 +314,7 @@ class Lbann(CachedCMakePackage, CudaPackage, ROCmPackage):
depends_on("py-protobuf+cpp@3.10.0:", type=("build", "run"), when="@:0.90,0.99: +pfe")
depends_on("protobuf+shared@3.10.0:", when="@:0.90,0.99:")
depends_on("zlib-api", when="protobuf@3.11.0:")
depends_on("zlib-api", when="^protobuf@3.11.0:")
# using cereal@1.3.1 and above requires changing the
# find_package call to lowercase, so stick with :1.3.0

View File

@@ -17,7 +17,7 @@ class PyAbipy(PythonPackage):
version("0.2.0", sha256="c72b796ba0f9ea4299eac3085bede092d2652e9e5e8074d3badd19ef7b600792")
variant("gui", default=False, description="Build the GUI")
variant("ipython", default=False, when="0.2.0", description="Build IPython support")
variant("ipython", default=False, when="@0.2.0", description="Build IPython support")
depends_on("py-setuptools", type="build")
# in newer pip versions --install-option does not exist

View File

@@ -32,7 +32,7 @@ class PyKombu(PythonPackage):
depends_on("py-amqp@5.0.0:5", when="@5.0.0:5.0.2", type=("build", "run"))
depends_on("py-amqp@5.0.9:5.0", when="@5.2.3", type=("build", "run"))
depends_on("py-vine", when="@5.1.0:", type=("build", "run"))
depends_on("py-importlib-metadata@0.18:", type=("build", "run"), when="python@:3.7")
depends_on("py-cached-property", type=("build", "run"), when="python@:3.7")
depends_on("py-importlib-metadata@0.18:", type=("build", "run"), when="^python@:3.7")
depends_on("py-cached-property", type=("build", "run"), when="^python@:3.7")
depends_on("py-redis@3.4.1:3,4.0.2:", when="+redis", type=("build", "run"))

View File

@@ -170,20 +170,20 @@ class PyNvidiaDali(PythonPackage):
)
cuda120_versions = (
"1.27.0-cuda120",
"1.26.0-cuda120",
"1.25.0-cuda120",
"1.24.0-cuda120",
"1.23.0-cuda120",
"1.22.0-cuda120",
"@1.27.0-cuda120",
"@1.26.0-cuda120",
"@1.25.0-cuda120",
"@1.24.0-cuda120",
"@1.23.0-cuda120",
"@1.22.0-cuda120",
)
cuda110_versions = (
"1.27.0-cuda110",
"1.26.0-cuda110",
"1.25.0-cuda110",
"1.24.0-cuda110",
"1.23.0-cuda110",
"1.22.0-cuda110",
"@1.27.0-cuda110",
"@1.26.0-cuda110",
"@1.25.0-cuda110",
"@1.24.0-cuda110",
"@1.23.0-cuda110",
"@1.22.0-cuda110",
)
for v in cuda120_versions:

View File

@@ -18,6 +18,6 @@ class PyPdbfixer(PythonPackage):
version("1.7", sha256="a0bef3c52a7bbe69a6aea5333f51f3e7d158339be5829aed19b0344bd66d4eea")
depends_on("py-setuptools", type="build")
depends_on("openmm@7.1:7.5", type=("build", "run"), when="1.7")
depends_on("openmm@7.1:7.5", type=("build", "run"), when="@1.7")
depends_on("openmm@7.6:", type=("build", "run"), when="@1.8:")
depends_on("py-numpy", type=("build", "run"))

View File

@@ -29,5 +29,5 @@ class PyTensorflowDatasets(PythonPackage):
depends_on("py-tensorflow-metadata", type=("build", "run"))
depends_on("py-termcolor", type=("build", "run"))
depends_on("py-tqdm", type=("build", "run"))
depends_on("py-typing-extensions", type=("build", "run"), when="python@:3.7")
depends_on("py-importlib-resources", type=("build", "run"), when="python@:3.8")
depends_on("py-typing-extensions", type=("build", "run"), when="^python@:3.7")
depends_on("py-importlib-resources", type=("build", "run"), when="^python@:3.8")

View File

@@ -105,6 +105,8 @@ class Root(CMakePackage):
when="@6.26:6.26.06 +root7 ^nlohmann-json@3.11:",
)
patch("webgui.patch", level=0, when="@6.26.00:6.26.10,6.28.00:6.28.08 +webgui")
if sys.platform == "darwin":
# Resolve non-standard use of uint, _cf_
# https://sft.its.cern.ch/jira/browse/ROOT-7886.

View File

@@ -0,0 +1,25 @@
--- config/rootrc.in_ORIG 2023-11-28 08:32:42.696061390 +0100
+++ config/rootrc.in 2023-11-28 08:32:47.672727920 +0100
@@ -217,7 +217,7 @@
# Type of tree viewer: TTreeViewer or RTreeViewer
TreeViewer.Name: TTreeViewer
# Type of Browser: TRootBrowser or TRootBrowserLite
-Browser.Name: @root_browser_class@
+Browser.Name: TRootBrowser
# Browser Options (plugins)
# F: File browser E: Text Editor H: HTML browser
# C: Canvas I: I/O redirection P: Proof G: GL viewer
--- config/rootrc.in_ORIG 2023-11-28 08:18:11.686085190 +0100
+++ config/rootrc.in 2023-11-28 08:18:15.839418409 +0100
@@ -247,8 +247,8 @@
WebGui.HttpPortMax: 9800
# Exact IP iddress to bind bind http server (default - empty)
WebGui.HttpBind:
-# Use only loopback address to bind http server (default - no)
-WebGui.HttpLoopback: no
+# Use only loopback address to bind http server (default - yes)
+WebGui.HttpLoopback: yes
# Use https protocol for the http server (default - no)
WebGui.UseHttps: no
WebGui.ServerCert: rootserver.pem