Compare commits
98 Commits
develop
...
releases/v
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6467ddefa3 | ||
![]() |
f0fee0c223 | ||
![]() |
4668616f55 | ||
![]() |
280a77424d | ||
![]() |
71fc3e74bd | ||
![]() |
80fbef84bd | ||
![]() |
bd9186038a | ||
![]() |
4da21d8f60 | ||
![]() |
581a339cf9 | ||
![]() |
45f673b822 | ||
![]() |
4a9a5b6eed | ||
![]() |
a2b92822d4 | ||
![]() |
0b79ead099 | ||
![]() |
8fe02b8d50 | ||
![]() |
3530e44c02 | ||
![]() |
532d844f26 | ||
![]() |
907238a7e8 | ||
![]() |
ed816d3f0c | ||
![]() |
d2096e8fc1 | ||
![]() |
0c766f2e9b | ||
![]() |
89319413d5 | ||
![]() |
a6ef73f7f2 | ||
![]() |
4a84039795 | ||
![]() |
e5f4c6ad75 | ||
![]() |
c0c66a0fed | ||
![]() |
3d73193ecc | ||
![]() |
eb27ee7ae8 | ||
![]() |
2883559b49 | ||
![]() |
7ce9f621d9 | ||
![]() |
0616290c7f | ||
![]() |
9963e2a20c | ||
![]() |
cb4312996c | ||
![]() |
b107de072b | ||
![]() |
dd58e922e7 | ||
![]() |
b23a829c4c | ||
![]() |
9af5eca9ec | ||
![]() |
2489b137d9 | ||
![]() |
64d046100a | ||
![]() |
000fe1b5a1 | ||
![]() |
e30fedab10 | ||
![]() |
a7c6df1b5a | ||
![]() |
19e86088cd | ||
![]() |
0295b466d7 | ||
![]() |
68e9547615 | ||
![]() |
0ab9c8b904 | ||
![]() |
dd768bb6c3 | ||
![]() |
b15f9d011c | ||
![]() |
4f7cce68b8 | ||
![]() |
f39a1e5fc8 | ||
![]() |
df2a3bd531 | ||
![]() |
e29049d9c0 | ||
![]() |
de2249c334 | ||
![]() |
03d7643480 | ||
![]() |
9f2b8eef7a | ||
![]() |
f57ac8d2da | ||
![]() |
539fa5c39a | ||
![]() |
50710c2b6e | ||
![]() |
f1b2515ce1 | ||
![]() |
37f65e769c | ||
![]() |
16b600c193 | ||
![]() |
bb62f71aa0 | ||
![]() |
fff8e16cdd | ||
![]() |
33c287eed8 | ||
![]() |
2d5ccd3068 | ||
![]() |
125085c580 | ||
![]() |
e70e401be1 | ||
![]() |
e8bbd7763c | ||
![]() |
712db8c85c | ||
![]() |
47c560d526 | ||
![]() |
5b386cf9b1 | ||
![]() |
2cbe84b1ee | ||
![]() |
e1f98fd206 | ||
![]() |
d5ef4f8c83 | ||
![]() |
b49022822d | ||
![]() |
198bd87914 | ||
![]() |
080a781b81 | ||
![]() |
65d3221a9c | ||
![]() |
f6b17f6329 | ||
![]() |
09e9bb5c3d | ||
![]() |
f6dc557764 | ||
![]() |
74172fb9d2 | ||
![]() |
c266e69cde | ||
![]() |
fe57ec2ab7 | ||
![]() |
3c3476a176 | ||
![]() |
67f20c3e5c | ||
![]() |
1baf712b87 | ||
![]() |
c73ec0b36d | ||
![]() |
9d58d5e645 | ||
![]() |
fc5fd7fc60 | ||
![]() |
efa59510a8 | ||
![]() |
6094ee5eae | ||
![]() |
f7cacdbf40 | ||
![]() |
5152738084 | ||
![]() |
9ba8d60789 | ||
![]() |
3c8cd8d30c | ||
![]() |
53a31bbbbf | ||
![]() |
bac314a4f1 | ||
![]() |
10ba172611 |
2
.github/workflows/bootstrap.yml
vendored
2
.github/workflows/bootstrap.yml
vendored
@ -176,7 +176,7 @@ jobs:
|
||||
runs-on: ${{ matrix.macos-version }}
|
||||
strategy:
|
||||
matrix:
|
||||
macos-version: ['macos-11', 'macos-12']
|
||||
macos-version: ['macos-12']
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
342
CHANGELOG.md
342
CHANGELOG.md
@ -1,3 +1,345 @@
|
||||
# v0.21.3 (2024-10-02)
|
||||
|
||||
## Bugfixes
|
||||
- Forward compatibility with Spack 0.23 packages with language dependencies (#45205, #45191)
|
||||
- Forward compatibility with `urllib` from Python 3.12.6+ (#46453, #46483)
|
||||
- Bump `archspec` to 0.2.5-dev for better aarch64 and Windows support (#42854, #44005,
|
||||
#45721, #46445)
|
||||
- Support macOS Sequoia (#45018, #45127, #43862)
|
||||
- CI and test maintenance (#42909, #42728, #46711, #41943, #43363)
|
||||
|
||||
# v0.21.2 (2024-03-01)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Containerize: accommodate nested or pre-existing spack-env paths (#41558)
|
||||
- Fix setup-env script, when going back and forth between instances (#40924)
|
||||
- Fix using fully-qualified namespaces from root specs (#41957)
|
||||
- Fix a bug when a required provider is requested for multiple virtuals (#42088)
|
||||
- OCI buildcaches:
|
||||
- only push in parallel when forking (#42143)
|
||||
- use pickleable errors (#42160)
|
||||
- Fix using sticky variants in externals (#42253)
|
||||
- Fix a rare issue with conditional requirements and multi-valued variants (#42566)
|
||||
|
||||
## Package updates
|
||||
- rust: add v1.75, rework a few variants (#41161,#41903)
|
||||
- py-transformers: add v4.35.2 (#41266)
|
||||
- mgard: fix OpenMP on AppleClang (#42933)
|
||||
|
||||
# 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.
|
||||
|
||||
## Features in this release
|
||||
|
||||
1. **Better error messages with condition chaining**
|
||||
|
||||
In v0.18, we added better error messages that could tell you what problem happened,
|
||||
but they couldn't tell you *why* it happened. `0.21` adds *condition chaining* to the
|
||||
solver, and Spack can now trace back through the conditions that led to an error and
|
||||
build a tree of causes potential causes and where they came from. For example:
|
||||
|
||||
```console
|
||||
$ spack solve hdf5 ^cmake@3.0.1
|
||||
==> Error: concretization failed for the following reasons:
|
||||
|
||||
1. Cannot satisfy 'cmake@3.0.1'
|
||||
2. Cannot satisfy 'cmake@3.0.1'
|
||||
required because hdf5 ^cmake@3.0.1 requested from CLI
|
||||
3. Cannot satisfy 'cmake@3.18:' and 'cmake@3.0.1
|
||||
required because hdf5 ^cmake@3.0.1 requested from CLI
|
||||
required because hdf5 depends on cmake@3.18: when @1.13:
|
||||
required because hdf5 ^cmake@3.0.1 requested from CLI
|
||||
4. Cannot satisfy 'cmake@3.12:' and 'cmake@3.0.1
|
||||
required because hdf5 depends on cmake@3.12:
|
||||
required because hdf5 ^cmake@3.0.1 requested from CLI
|
||||
required because hdf5 ^cmake@3.0.1 requested from CLI
|
||||
```
|
||||
|
||||
More details in #40173.
|
||||
|
||||
2. **OCI build caches**
|
||||
|
||||
You can now use an arbitrary [OCI](https://opencontainers.org) registry as a build
|
||||
cache:
|
||||
|
||||
```console
|
||||
$ spack mirror add my_registry oci://user/image # Dockerhub
|
||||
$ spack mirror add my_registry oci://ghcr.io/haampie/spack-test # GHCR
|
||||
$ spack mirror set --push --oci-username ... --oci-password ... my_registry # set login creds
|
||||
$ spack buildcache push my_registry [specs...]
|
||||
```
|
||||
|
||||
And you can optionally add a base image to get *runnable* images:
|
||||
|
||||
```console
|
||||
$ spack buildcache push --base-image ubuntu:23.04 my_registry python
|
||||
Pushed ... as [image]:python-3.11.2-65txfcpqbmpawclvtasuog4yzmxwaoia.spack
|
||||
|
||||
$ docker run --rm -it [image]:python-3.11.2-65txfcpqbmpawclvtasuog4yzmxwaoia.spack
|
||||
```
|
||||
|
||||
This creates a container image from the Spack installations on the host system,
|
||||
without the need to run `spack install` from a `Dockerfile` or `sif` file. It also
|
||||
addresses the inconvenience of losing binaries of dependencies when `RUN spack
|
||||
install` fails inside `docker build`.
|
||||
|
||||
Further, the container image layers and build cache tarballs are the same files. This
|
||||
means that `spack install` and `docker pull` use the exact same underlying binaries.
|
||||
If you previously used `spack install` inside of `docker build`, this feature helps
|
||||
you save storage by a factor two.
|
||||
|
||||
More details in #38358.
|
||||
|
||||
3. **Multiple versions of build dependencies**
|
||||
|
||||
Increasingly, complex package builds require multiple versions of some build
|
||||
dependencies. For example, Python packages frequently require very specific versions
|
||||
of `setuptools`, `cython`, and sometimes different physics packages require different
|
||||
versions of Python to build. The concretizer enforced that every solve was *unified*,
|
||||
i.e., that there only be one version of every package. The concretizer now supports
|
||||
"duplicate" nodes for *build dependencies*, but enforces unification through
|
||||
transitive link and run dependencies. This will allow it to better resolve complex
|
||||
dependency graphs in ecosystems like Python, and it also gets us very close to
|
||||
modeling compilers as proper dependencies.
|
||||
|
||||
This change required a major overhaul of the concretizer, as well as a number of
|
||||
performance optimizations. See #38447, #39621.
|
||||
|
||||
4. **Cherry-picking virtual dependencies**
|
||||
|
||||
You can now select only a subset of virtual dependencies from a spec that may provide
|
||||
more. For example, if you want `mpich` to be your `mpi` provider, you can be explicit
|
||||
by writing:
|
||||
|
||||
```
|
||||
hdf5 ^[virtuals=mpi] mpich
|
||||
```
|
||||
|
||||
Or, if you want to use, e.g., `intel-parallel-studio` for `blas` along with an external
|
||||
`lapack` like `openblas`, you could write:
|
||||
|
||||
```
|
||||
strumpack ^[virtuals=mpi] intel-parallel-studio+mkl ^[virtuals=lapack] openblas
|
||||
```
|
||||
|
||||
The `virtuals=mpi` is an edge attribute, and dependency edges in Spack graphs now
|
||||
track which virtuals they satisfied. More details in #17229 and #35322.
|
||||
|
||||
Note for packaging: in Spack 0.21 `spec.satisfies("^virtual")` is true if and only if
|
||||
the package specifies `depends_on("virtual")`. This is different from Spack 0.20,
|
||||
where depending on a provider implied depending on the virtual provided. See #41002
|
||||
for an example where `^mkl` was being used to test for several `mkl` providers in a
|
||||
package that did not depend on `mkl`.
|
||||
|
||||
5. **License directive**
|
||||
|
||||
Spack packages can now have license metadata, with the new `license()` directive:
|
||||
|
||||
```python
|
||||
license("Apache-2.0")
|
||||
```
|
||||
|
||||
Licenses use [SPDX identifiers](https://spdx.org/licenses), and you can use SPDX
|
||||
expressions to combine them:
|
||||
|
||||
```python
|
||||
license("Apache-2.0 OR MIT")
|
||||
```
|
||||
|
||||
Like other directives in Spack, it's conditional, so you can handle complex cases like
|
||||
Spack itself:
|
||||
|
||||
```python
|
||||
license("LGPL-2.1", when="@:0.11")
|
||||
license("Apache-2.0 OR MIT", when="@0.12:")
|
||||
```
|
||||
|
||||
More details in #39346, #40598.
|
||||
|
||||
6. **`spack deconcretize` command**
|
||||
|
||||
We are getting close to having a `spack update` command for environments, but we're
|
||||
not quite there yet. This is the next best thing. `spack deconcretize` gives you
|
||||
control over what you want to update in an already concrete environment. If you have
|
||||
an environment built with, say, `meson`, and you want to update your `meson` version,
|
||||
you can run:
|
||||
|
||||
```console
|
||||
spack deconcretize meson
|
||||
```
|
||||
|
||||
and have everything that depends on `meson` rebuilt the next time you run `spack
|
||||
concretize`. In a future Spack version, we'll handle all of this in a single command,
|
||||
but for now you can use this to drop bits of your lockfile and resolve your
|
||||
dependencies again. More in #38803.
|
||||
|
||||
7. **UI Improvements**
|
||||
|
||||
The venerable `spack info` command was looking shabby compared to the rest of Spack's
|
||||
UI, so we reworked it to have a bit more flair. `spack info` now makes much better
|
||||
use of terminal space and shows variants, their values, and their descriptions much
|
||||
more clearly. Conditional variants are grouped separately so you can more easily
|
||||
understand how packages are structured. More in #40998.
|
||||
|
||||
`spack checksum` now allows you to filter versions from your editor, or by version
|
||||
range. It also notifies you about potential download URL changes. See #40403.
|
||||
|
||||
8. **Environments can include definitions**
|
||||
|
||||
Spack did not previously support using `include:` with The
|
||||
[definitions](https://spack.readthedocs.io/en/latest/environments.html#spec-list-references)
|
||||
section of an environment, but now it does. You can use this to curate lists of specs
|
||||
and more easily reuse them across environments. See #33960.
|
||||
|
||||
9. **Aliases**
|
||||
|
||||
You can now add aliases to Spack commands in `config.yaml`, e.g. this might enshrine
|
||||
your favorite args to `spack find` as `spack f`:
|
||||
|
||||
```yaml
|
||||
config:
|
||||
aliases:
|
||||
f: find -lv
|
||||
```
|
||||
|
||||
See #17229.
|
||||
|
||||
10. **Improved autoloading of modules**
|
||||
|
||||
Spack 0.20 was the first release to enable autoloading of direct dependencies in
|
||||
module files.
|
||||
|
||||
The downside of this was that `module avail` and `module load` tab completion would
|
||||
show users too many modules to choose from, and many users disabled generating
|
||||
modules for dependencies through `exclude_implicits: true`. Further, it was
|
||||
necessary to keep hashes in module names to avoid file name clashes.
|
||||
|
||||
In this release, you can start using `hide_implicits: true` instead, which exposes
|
||||
only explicitly installed packages to the user, while still autoloading
|
||||
dependencies. On top of that, you can safely use `hash_length: 0`, as this config
|
||||
now only applies to the modules exposed to the user -- you don't have to worry about
|
||||
file name clashes for hidden dependencies.
|
||||
|
||||
Note: for `tcl` this feature requires Modules 4.7 or higher
|
||||
|
||||
11. **Updated container labeling**
|
||||
|
||||
Nightly Docker images from the `develop` branch will now be tagged as `:develop` and
|
||||
`:nightly`. The `:latest` tag is no longer associated with `:develop`, but with the
|
||||
latest stable release. Releases will be tagged with `:{major}`, `:{major}.{minor}`
|
||||
and `:{major}.{minor}.{patch}`. `ubuntu:18.04` has also been removed from the list of
|
||||
generated Docker images, as it is no longer supported. See #40593.
|
||||
|
||||
## Other new commands and directives
|
||||
|
||||
* `spack env activate` without arguments now loads a `default` environment that you do
|
||||
not have to create (#40756).
|
||||
* `spack find -H` / `--hashes`: a new shortcut for piping `spack find` output to
|
||||
other commands (#38663)
|
||||
* Add `spack checksum --verify`, fix `--add` (#38458)
|
||||
* New `default_args` context manager factors out common args for directives (#39964)
|
||||
* `spack compiler find --[no]-mixed-toolchain` lets you easily mix `clang` and
|
||||
`gfortran` on Linux (#40902)
|
||||
|
||||
## Performance improvements
|
||||
|
||||
* `spack external find` execution is now much faster (#39843)
|
||||
* `spack location -i` now much faster on success (#40898)
|
||||
* Drop redundant rpaths post install (#38976)
|
||||
* ASP-based solver: avoid cycles in clingo using hidden directive (#40720)
|
||||
* Fix multiple quadratic complexity issues in environments (#38771)
|
||||
|
||||
## Other new features of note
|
||||
|
||||
* archspec: update to v0.2.2, support for Sapphire Rapids, Power10, Neoverse V2 (#40917)
|
||||
* Propagate variants across nodes that don't have that variant (#38512)
|
||||
* Implement fish completion (#29549)
|
||||
* Can now distinguish between source/binary mirror; don't ping mirror.spack.io as much (#34523)
|
||||
* Improve status reporting on install (add [n/total] display) (#37903)
|
||||
|
||||
## Windows
|
||||
|
||||
This release has the best Windows support of any Spack release yet, with numerous
|
||||
improvements and much larger swaths of tests passing:
|
||||
|
||||
* MSVC and SDK improvements (#37711, #37930, #38500, #39823, #39180)
|
||||
* Windows external finding: update default paths; treat .bat as executable on Windows (#39850)
|
||||
* Windows decompression: fix removal of intermediate file (#38958)
|
||||
* Windows: executable/path handling (#37762)
|
||||
* Windows build systems: use ninja and enable tests (#33589)
|
||||
* Windows testing (#36970, #36972, #36973, #36840, #36977, #36792, #36834, #34696, #36971)
|
||||
* Windows PowerShell support (#39118, #37951)
|
||||
* Windows symlinking and libraries (#39933, #38599, #34701, #38578, #34701)
|
||||
|
||||
## Notable refactors
|
||||
* User-specified flags take precedence over others in Spack compiler wrappers (#37376)
|
||||
* Improve setup of build, run, and test environments (#35737, #40916)
|
||||
* `make` is no longer a required system dependency of Spack (#40380)
|
||||
* Support Python 3.12 (#40404, #40155, #40153)
|
||||
* docs: Replace package list with packages.spack.io (#40251)
|
||||
* Drop Python 2 constructs in Spack (#38720, #38718, #38703)
|
||||
|
||||
## Binary cache and stack updates
|
||||
* e4s arm stack: duplicate and target neoverse v1 (#40369)
|
||||
* Add macOS ML CI stacks (#36586)
|
||||
* E4S Cray CI Stack (#37837)
|
||||
* e4s cray: expand spec list (#38947)
|
||||
* e4s cray sles ci: expand spec list (#39081)
|
||||
|
||||
## Removals, deprecations, and syntax changes
|
||||
* ASP: targets, compilers and providers soft-preferences are only global (#31261)
|
||||
* Parser: fix ambiguity with whitespace in version ranges (#40344)
|
||||
* Module file generation is disabled by default; you'll need to enable it to use it (#37258)
|
||||
* Remove deprecated "extra_instructions" option for containers (#40365)
|
||||
* Stand-alone test feature deprecation postponed to v0.22 (#40600)
|
||||
* buildcache push: make `--allow-root` the default and deprecate the option (#38878)
|
||||
|
||||
## Notable Bugfixes
|
||||
* Bugfix: propagation of multivalued variants (#39833)
|
||||
* Allow `/` in git versions (#39398)
|
||||
* Fetch & patch: actually acquire stage lock, and many more issues (#38903)
|
||||
* Environment/depfile: better escaping of targets with Git versions (#37560)
|
||||
* Prevent "spack external find" to error out on wrong permissions (#38755)
|
||||
* lmod: allow core compiler to be specified with a version range (#37789)
|
||||
|
||||
## Spack community stats
|
||||
|
||||
* 7,469 total packages, 303 new since `v0.20.0`
|
||||
* 150 new Python packages
|
||||
* 34 new R packages
|
||||
* 353 people contributed to this release
|
||||
* 336 committers to packages
|
||||
* 65 committers to core
|
||||
|
||||
|
||||
# v0.20.3 (2023-10-31)
|
||||
|
||||
## Bugfixes
|
||||
|
@ -46,12 +46,10 @@ modules:
|
||||
tcl:
|
||||
all:
|
||||
autoload: direct
|
||||
hide_implicits: true
|
||||
|
||||
# Default configurations if lmod is enabled
|
||||
lmod:
|
||||
all:
|
||||
autoload: direct
|
||||
hide_implicits: true
|
||||
hierarchy:
|
||||
- mpi
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
77
lib/spack/docs/frequently_asked_questions.rst
Normal file
77
lib/spack/docs/frequently_asked_questions.rst
Normal 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
|
@ -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
|
||||
|
560
lib/spack/docs/packages_yaml.rst
Normal file
560
lib/spack/docs/packages_yaml.rst
Normal file
@ -0,0 +1,560 @@
|
||||
.. 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:
|
||||
package_attributes:
|
||||
# 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.
|
2
lib/spack/external/__init__.py
vendored
2
lib/spack/external/__init__.py
vendored
@ -18,7 +18,7 @@
|
||||
|
||||
* Homepage: https://pypi.python.org/pypi/archspec
|
||||
* Usage: Labeling, comparison and detection of microarchitectures
|
||||
* Version: 0.2.2 (commit 1dc58a5776dd77e6fc6e4ba5626af5b1fb24996e)
|
||||
* Version: 0.2.5-dev (commit cbb1fd5eb397a70d466e5160b393b87b0dbcc78f)
|
||||
|
||||
astunparse
|
||||
----------------
|
||||
|
3
lib/spack/external/archspec/__init__.py
vendored
3
lib/spack/external/archspec/__init__.py
vendored
@ -1,2 +1,3 @@
|
||||
"""Init file to avoid namespace packages"""
|
||||
__version__ = "0.2.2"
|
||||
|
||||
__version__ = "0.2.4"
|
||||
|
1
lib/spack/external/archspec/__main__.py
vendored
1
lib/spack/external/archspec/__main__.py
vendored
@ -3,6 +3,7 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from .cli import main
|
||||
|
||||
sys.exit(main())
|
||||
|
6
lib/spack/external/archspec/cli.py
vendored
6
lib/spack/external/archspec/cli.py
vendored
@ -46,7 +46,11 @@ def _make_parser() -> argparse.ArgumentParser:
|
||||
|
||||
def cpu() -> int:
|
||||
"""Run the `archspec cpu` subcommand."""
|
||||
print(archspec.cpu.host())
|
||||
try:
|
||||
print(archspec.cpu.host())
|
||||
except FileNotFoundError as exc:
|
||||
print(exc)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
|
13
lib/spack/external/archspec/cpu/__init__.py
vendored
13
lib/spack/external/archspec/cpu/__init__.py
vendored
@ -5,10 +5,14 @@
|
||||
"""The "cpu" package permits to query and compare different
|
||||
CPU microarchitectures.
|
||||
"""
|
||||
from .microarchitecture import Microarchitecture, UnsupportedMicroarchitecture
|
||||
from .microarchitecture import TARGETS, generic_microarchitecture
|
||||
from .microarchitecture import version_components
|
||||
from .detect import host
|
||||
from .detect import brand_string, host
|
||||
from .microarchitecture import (
|
||||
TARGETS,
|
||||
Microarchitecture,
|
||||
UnsupportedMicroarchitecture,
|
||||
generic_microarchitecture,
|
||||
version_components,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"Microarchitecture",
|
||||
@ -17,4 +21,5 @@
|
||||
"generic_microarchitecture",
|
||||
"host",
|
||||
"version_components",
|
||||
"brand_string",
|
||||
]
|
||||
|
420
lib/spack/external/archspec/cpu/detect.py
vendored
420
lib/spack/external/archspec/cpu/detect.py
vendored
@ -4,15 +4,17 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
"""Detection of CPU microarchitectures"""
|
||||
import collections
|
||||
import functools
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import struct
|
||||
import subprocess
|
||||
import warnings
|
||||
from typing import Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
from .microarchitecture import generic_microarchitecture, TARGETS
|
||||
from .schema import TARGETS_JSON
|
||||
from ..vendor.cpuid.cpuid import CPUID
|
||||
from .microarchitecture import TARGETS, Microarchitecture, generic_microarchitecture
|
||||
from .schema import CPUID_JSON, TARGETS_JSON
|
||||
|
||||
#: Mapping from operating systems to chain of commands
|
||||
#: to obtain a dictionary of raw info on the current cpu
|
||||
@ -22,43 +24,51 @@
|
||||
#: functions checking the compatibility of the host with a given target
|
||||
COMPATIBILITY_CHECKS = {}
|
||||
|
||||
# Constants for commonly used architectures
|
||||
X86_64 = "x86_64"
|
||||
AARCH64 = "aarch64"
|
||||
PPC64LE = "ppc64le"
|
||||
PPC64 = "ppc64"
|
||||
RISCV64 = "riscv64"
|
||||
|
||||
def info_dict(operating_system):
|
||||
"""Decorator to mark functions that are meant to return raw info on
|
||||
the current cpu.
|
||||
|
||||
def detection(operating_system: str):
|
||||
"""Decorator to mark functions that are meant to return partial information on the current cpu.
|
||||
|
||||
Args:
|
||||
operating_system (str or tuple): operating system for which the marked
|
||||
function is a viable factory of raw info dictionaries.
|
||||
operating_system: operating system where this function can be used.
|
||||
"""
|
||||
|
||||
def decorator(factory):
|
||||
INFO_FACTORY[operating_system].append(factory)
|
||||
|
||||
@functools.wraps(factory)
|
||||
def _impl():
|
||||
info = factory()
|
||||
|
||||
# Check that info contains a few mandatory fields
|
||||
msg = 'field "{0}" is missing from raw info dictionary'
|
||||
assert "vendor_id" in info, msg.format("vendor_id")
|
||||
assert "flags" in info, msg.format("flags")
|
||||
assert "model" in info, msg.format("model")
|
||||
assert "model_name" in info, msg.format("model_name")
|
||||
|
||||
return info
|
||||
|
||||
return _impl
|
||||
return factory
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@info_dict(operating_system="Linux")
|
||||
def proc_cpuinfo():
|
||||
"""Returns a raw info dictionary by parsing the first entry of
|
||||
``/proc/cpuinfo``
|
||||
"""
|
||||
info = {}
|
||||
def partial_uarch(
|
||||
name: str = "",
|
||||
vendor: str = "",
|
||||
features: Optional[Set[str]] = None,
|
||||
generation: int = 0,
|
||||
cpu_part: str = "",
|
||||
) -> Microarchitecture:
|
||||
"""Construct a partial microarchitecture, from information gathered during system scan."""
|
||||
return Microarchitecture(
|
||||
name=name,
|
||||
parents=[],
|
||||
vendor=vendor,
|
||||
features=features or set(),
|
||||
compilers={},
|
||||
generation=generation,
|
||||
cpu_part=cpu_part,
|
||||
)
|
||||
|
||||
|
||||
@detection(operating_system="Linux")
|
||||
def proc_cpuinfo() -> Microarchitecture:
|
||||
"""Returns a partial Microarchitecture, obtained from scanning ``/proc/cpuinfo``"""
|
||||
data = {}
|
||||
with open("/proc/cpuinfo") as file: # pylint: disable=unspecified-encoding
|
||||
for line in file:
|
||||
key, separator, value = line.partition(":")
|
||||
@ -70,11 +80,122 @@ def proc_cpuinfo():
|
||||
#
|
||||
# we are on a blank line separating two cpus. Exit early as
|
||||
# we want to read just the first entry in /proc/cpuinfo
|
||||
if separator != ":" and info:
|
||||
if separator != ":" and data:
|
||||
break
|
||||
|
||||
info[key.strip()] = value.strip()
|
||||
return info
|
||||
data[key.strip()] = value.strip()
|
||||
|
||||
architecture = _machine()
|
||||
if architecture == X86_64:
|
||||
return partial_uarch(
|
||||
vendor=data.get("vendor_id", "generic"), features=_feature_set(data, key="flags")
|
||||
)
|
||||
|
||||
if architecture == AARCH64:
|
||||
return partial_uarch(
|
||||
vendor=_canonicalize_aarch64_vendor(data),
|
||||
features=_feature_set(data, key="Features"),
|
||||
cpu_part=data.get("CPU part", ""),
|
||||
)
|
||||
|
||||
if architecture in (PPC64LE, PPC64):
|
||||
generation_match = re.search(r"POWER(\d+)", data.get("cpu", ""))
|
||||
try:
|
||||
generation = int(generation_match.group(1))
|
||||
except AttributeError:
|
||||
# There might be no match under emulated environments. For instance
|
||||
# emulating a ppc64le with QEMU and Docker still reports the host
|
||||
# /proc/cpuinfo and not a Power
|
||||
generation = 0
|
||||
return partial_uarch(generation=generation)
|
||||
|
||||
if architecture == RISCV64:
|
||||
if data.get("uarch") == "sifive,u74-mc":
|
||||
data["uarch"] = "u74mc"
|
||||
return partial_uarch(name=data.get("uarch", RISCV64))
|
||||
|
||||
return generic_microarchitecture(architecture)
|
||||
|
||||
|
||||
class CpuidInfoCollector:
|
||||
"""Collects the information we need on the host CPU from cpuid"""
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
def __init__(self):
|
||||
self.cpuid = CPUID()
|
||||
|
||||
registers = self.cpuid.registers_for(**CPUID_JSON["vendor"]["input"])
|
||||
self.highest_basic_support = registers.eax
|
||||
self.vendor = struct.pack("III", registers.ebx, registers.edx, registers.ecx).decode(
|
||||
"utf-8"
|
||||
)
|
||||
|
||||
registers = self.cpuid.registers_for(**CPUID_JSON["highest_extension_support"]["input"])
|
||||
self.highest_extension_support = registers.eax
|
||||
|
||||
self.features = self._features()
|
||||
|
||||
def _features(self):
|
||||
result = set()
|
||||
|
||||
def check_features(data):
|
||||
registers = self.cpuid.registers_for(**data["input"])
|
||||
for feature_check in data["bits"]:
|
||||
current = getattr(registers, feature_check["register"])
|
||||
if self._is_bit_set(current, feature_check["bit"]):
|
||||
result.add(feature_check["name"])
|
||||
|
||||
for call_data in CPUID_JSON["flags"]:
|
||||
if call_data["input"]["eax"] > self.highest_basic_support:
|
||||
continue
|
||||
check_features(call_data)
|
||||
|
||||
for call_data in CPUID_JSON["extension-flags"]:
|
||||
if call_data["input"]["eax"] > self.highest_extension_support:
|
||||
continue
|
||||
check_features(call_data)
|
||||
|
||||
return result
|
||||
|
||||
def _is_bit_set(self, register: int, bit: int) -> bool:
|
||||
mask = 1 << bit
|
||||
return register & mask > 0
|
||||
|
||||
def brand_string(self) -> Optional[str]:
|
||||
"""Returns the brand string, if available."""
|
||||
if self.highest_extension_support < 0x80000004:
|
||||
return None
|
||||
|
||||
r1 = self.cpuid.registers_for(eax=0x80000002, ecx=0)
|
||||
r2 = self.cpuid.registers_for(eax=0x80000003, ecx=0)
|
||||
r3 = self.cpuid.registers_for(eax=0x80000004, ecx=0)
|
||||
result = struct.pack(
|
||||
"IIIIIIIIIIII",
|
||||
r1.eax,
|
||||
r1.ebx,
|
||||
r1.ecx,
|
||||
r1.edx,
|
||||
r2.eax,
|
||||
r2.ebx,
|
||||
r2.ecx,
|
||||
r2.edx,
|
||||
r3.eax,
|
||||
r3.ebx,
|
||||
r3.ecx,
|
||||
r3.edx,
|
||||
).decode("utf-8")
|
||||
return result.strip("\x00")
|
||||
|
||||
|
||||
@detection(operating_system="Windows")
|
||||
def cpuid_info():
|
||||
"""Returns a partial Microarchitecture, obtained from running the cpuid instruction"""
|
||||
architecture = _machine()
|
||||
if architecture == X86_64:
|
||||
data = CpuidInfoCollector()
|
||||
return partial_uarch(vendor=data.vendor, features=data.features)
|
||||
|
||||
return generic_microarchitecture(architecture)
|
||||
|
||||
|
||||
def _check_output(args, env):
|
||||
@ -83,14 +204,25 @@ def _check_output(args, env):
|
||||
return str(output.decode("utf-8"))
|
||||
|
||||
|
||||
WINDOWS_MAPPING = {
|
||||
"AMD64": X86_64,
|
||||
"ARM64": AARCH64,
|
||||
}
|
||||
|
||||
|
||||
def _machine():
|
||||
""" "Return the machine architecture we are on"""
|
||||
"""Return the machine architecture we are on"""
|
||||
operating_system = platform.system()
|
||||
|
||||
# If we are not on Darwin, trust what Python tells us
|
||||
if operating_system != "Darwin":
|
||||
# If we are not on Darwin or Windows, trust what Python tells us
|
||||
if operating_system not in ("Darwin", "Windows"):
|
||||
return platform.machine()
|
||||
|
||||
# Normalize windows specific names
|
||||
if operating_system == "Windows":
|
||||
platform_machine = platform.machine()
|
||||
return WINDOWS_MAPPING.get(platform_machine, platform_machine)
|
||||
|
||||
# On Darwin it might happen that we are on M1, but using an interpreter
|
||||
# built for x86_64. In that case "platform.machine() == 'x86_64'", so we
|
||||
# need to fix that.
|
||||
@ -103,54 +235,47 @@ def _machine():
|
||||
if "Apple" in output:
|
||||
# Note that a native Python interpreter on Apple M1 would return
|
||||
# "arm64" instead of "aarch64". Here we normalize to the latter.
|
||||
return "aarch64"
|
||||
return AARCH64
|
||||
|
||||
return "x86_64"
|
||||
return X86_64
|
||||
|
||||
|
||||
@info_dict(operating_system="Darwin")
|
||||
def sysctl_info_dict():
|
||||
@detection(operating_system="Darwin")
|
||||
def sysctl_info() -> Microarchitecture:
|
||||
"""Returns a raw info dictionary parsing the output of sysctl."""
|
||||
child_environment = _ensure_bin_usrbin_in_path()
|
||||
|
||||
def sysctl(*args):
|
||||
def sysctl(*args: str) -> str:
|
||||
return _check_output(["sysctl"] + list(args), env=child_environment).strip()
|
||||
|
||||
if _machine() == "x86_64":
|
||||
flags = (
|
||||
sysctl("-n", "machdep.cpu.features").lower()
|
||||
+ " "
|
||||
+ sysctl("-n", "machdep.cpu.leaf7_features").lower()
|
||||
if _machine() == X86_64:
|
||||
features = (
|
||||
f'{sysctl("-n", "machdep.cpu.features").lower()} '
|
||||
f'{sysctl("-n", "machdep.cpu.leaf7_features").lower()}'
|
||||
)
|
||||
info = {
|
||||
"vendor_id": sysctl("-n", "machdep.cpu.vendor"),
|
||||
"flags": flags,
|
||||
"model": sysctl("-n", "machdep.cpu.model"),
|
||||
"model name": sysctl("-n", "machdep.cpu.brand_string"),
|
||||
}
|
||||
else:
|
||||
model = "unknown"
|
||||
model_str = sysctl("-n", "machdep.cpu.brand_string").lower()
|
||||
if "m2" in model_str:
|
||||
model = "m2"
|
||||
elif "m1" in model_str:
|
||||
model = "m1"
|
||||
elif "apple" in model_str:
|
||||
model = "m1"
|
||||
features = set(features.split())
|
||||
|
||||
info = {
|
||||
"vendor_id": "Apple",
|
||||
"flags": [],
|
||||
"model": model,
|
||||
"CPU implementer": "Apple",
|
||||
"model name": sysctl("-n", "machdep.cpu.brand_string"),
|
||||
}
|
||||
return info
|
||||
# Flags detected on Darwin turned to their linux counterpart
|
||||
for darwin_flag, linux_flag in TARGETS_JSON["conversions"]["darwin_flags"].items():
|
||||
if darwin_flag in features:
|
||||
features.update(linux_flag.split())
|
||||
|
||||
return partial_uarch(vendor=sysctl("-n", "machdep.cpu.vendor"), features=features)
|
||||
|
||||
model = "unknown"
|
||||
model_str = sysctl("-n", "machdep.cpu.brand_string").lower()
|
||||
if "m2" in model_str:
|
||||
model = "m2"
|
||||
elif "m1" in model_str:
|
||||
model = "m1"
|
||||
elif "apple" in model_str:
|
||||
model = "m1"
|
||||
|
||||
return partial_uarch(name=model, vendor="Apple")
|
||||
|
||||
|
||||
def _ensure_bin_usrbin_in_path():
|
||||
# Make sure that /sbin and /usr/sbin are in PATH as sysctl is
|
||||
# usually found there
|
||||
# Make sure that /sbin and /usr/sbin are in PATH as sysctl is usually found there
|
||||
child_environment = dict(os.environ.items())
|
||||
search_paths = child_environment.get("PATH", "").split(os.pathsep)
|
||||
for additional_path in ("/sbin", "/usr/sbin"):
|
||||
@ -160,22 +285,10 @@ def _ensure_bin_usrbin_in_path():
|
||||
return child_environment
|
||||
|
||||
|
||||
def adjust_raw_flags(info):
|
||||
"""Adjust the flags detected on the system to homogenize
|
||||
slightly different representations.
|
||||
"""
|
||||
# Flags detected on Darwin turned to their linux counterpart
|
||||
flags = info.get("flags", [])
|
||||
d2l = TARGETS_JSON["conversions"]["darwin_flags"]
|
||||
for darwin_flag, linux_flag in d2l.items():
|
||||
if darwin_flag in flags:
|
||||
info["flags"] += " " + linux_flag
|
||||
|
||||
|
||||
def adjust_raw_vendor(info):
|
||||
"""Adjust the vendor field to make it human readable"""
|
||||
if "CPU implementer" not in info:
|
||||
return
|
||||
def _canonicalize_aarch64_vendor(data: Dict[str, str]) -> str:
|
||||
"""Adjust the vendor field to make it human-readable"""
|
||||
if "CPU implementer" not in data:
|
||||
return "generic"
|
||||
|
||||
# Mapping numeric codes to vendor (ARM). This list is a merge from
|
||||
# different sources:
|
||||
@ -185,43 +298,37 @@ def adjust_raw_vendor(info):
|
||||
# https://github.com/gcc-mirror/gcc/blob/master/gcc/config/aarch64/aarch64-cores.def
|
||||
# https://patchwork.kernel.org/patch/10524949/
|
||||
arm_vendors = TARGETS_JSON["conversions"]["arm_vendors"]
|
||||
arm_code = info["CPU implementer"]
|
||||
if arm_code in arm_vendors:
|
||||
info["CPU implementer"] = arm_vendors[arm_code]
|
||||
arm_code = data["CPU implementer"]
|
||||
return arm_vendors.get(arm_code, arm_code)
|
||||
|
||||
|
||||
def raw_info_dictionary():
|
||||
"""Returns a dictionary with information on the cpu of the current host.
|
||||
def _feature_set(data: Dict[str, str], key: str) -> Set[str]:
|
||||
return set(data.get(key, "").split())
|
||||
|
||||
This function calls all the viable factories one after the other until
|
||||
there's one that is able to produce the requested information.
|
||||
|
||||
def detected_info() -> Microarchitecture:
|
||||
"""Returns a partial Microarchitecture with information on the CPU of the current host.
|
||||
|
||||
This function calls all the viable factories one after the other until there's one that is
|
||||
able to produce the requested information. Falls-back to a generic microarchitecture, if none
|
||||
of the calls succeed.
|
||||
"""
|
||||
# pylint: disable=broad-except
|
||||
info = {}
|
||||
for factory in INFO_FACTORY[platform.system()]:
|
||||
try:
|
||||
info = factory()
|
||||
return factory()
|
||||
except Exception as exc:
|
||||
warnings.warn(str(exc))
|
||||
|
||||
if info:
|
||||
adjust_raw_flags(info)
|
||||
adjust_raw_vendor(info)
|
||||
break
|
||||
|
||||
return info
|
||||
return generic_microarchitecture(_machine())
|
||||
|
||||
|
||||
def compatible_microarchitectures(info):
|
||||
"""Returns an unordered list of known micro-architectures that are
|
||||
compatible with the info dictionary passed as argument.
|
||||
|
||||
Args:
|
||||
info (dict): dictionary containing information on the host cpu
|
||||
def compatible_microarchitectures(info: Microarchitecture) -> List[Microarchitecture]:
|
||||
"""Returns an unordered list of known micro-architectures that are compatible with the
|
||||
partial Microarchitecture passed as input.
|
||||
"""
|
||||
architecture_family = _machine()
|
||||
# If a tester is not registered, be conservative and assume no known
|
||||
# target is compatible with the host
|
||||
# If a tester is not registered, assume no known target is compatible with the host
|
||||
tester = COMPATIBILITY_CHECKS.get(architecture_family, lambda x, y: False)
|
||||
return [x for x in TARGETS.values() if tester(info, x)] or [
|
||||
generic_microarchitecture(architecture_family)
|
||||
@ -230,8 +337,8 @@ def compatible_microarchitectures(info):
|
||||
|
||||
def host():
|
||||
"""Detects the host micro-architecture and returns it."""
|
||||
# Retrieve a dictionary with raw information on the host's cpu
|
||||
info = raw_info_dictionary()
|
||||
# Retrieve information on the host's cpu
|
||||
info = detected_info()
|
||||
|
||||
# Get a list of possible candidates for this micro-architecture
|
||||
candidates = compatible_microarchitectures(info)
|
||||
@ -244,6 +351,10 @@ def sorting_fn(item):
|
||||
generic_candidates = [c for c in candidates if c.vendor == "generic"]
|
||||
best_generic = max(generic_candidates, key=sorting_fn)
|
||||
|
||||
# Relevant for AArch64. Filter on "cpu_part" if we have any match
|
||||
if info.cpu_part != "" and any(c for c in candidates if info.cpu_part == c.cpu_part):
|
||||
candidates = [c for c in candidates if info.cpu_part == c.cpu_part]
|
||||
|
||||
# Filter the candidates to be descendant of the best generic candidate.
|
||||
# This is to avoid that the lack of a niche feature that can be disabled
|
||||
# from e.g. BIOS prevents detection of a reasonably performant architecture
|
||||
@ -258,16 +369,15 @@ def sorting_fn(item):
|
||||
return max(candidates, key=sorting_fn)
|
||||
|
||||
|
||||
def compatibility_check(architecture_family):
|
||||
def compatibility_check(architecture_family: Union[str, Tuple[str, ...]]):
|
||||
"""Decorator to register a function as a proper compatibility check.
|
||||
|
||||
A compatibility check function takes the raw info dictionary as a first
|
||||
argument and an arbitrary target as the second argument. It returns True
|
||||
if the target is compatible with the info dictionary, False otherwise.
|
||||
A compatibility check function takes a partial Microarchitecture object as a first argument,
|
||||
and an arbitrary target Microarchitecture as the second argument. It returns True if the
|
||||
target is compatible with first argument, False otherwise.
|
||||
|
||||
Args:
|
||||
architecture_family (str or tuple): architecture family for which
|
||||
this test can be used, e.g. x86_64 or ppc64le etc.
|
||||
architecture_family: architecture family for which this test can be used
|
||||
"""
|
||||
# Turn the argument into something iterable
|
||||
if isinstance(architecture_family, str):
|
||||
@ -280,86 +390,70 @@ def decorator(func):
|
||||
return decorator
|
||||
|
||||
|
||||
@compatibility_check(architecture_family=("ppc64le", "ppc64"))
|
||||
@compatibility_check(architecture_family=(PPC64LE, PPC64))
|
||||
def compatibility_check_for_power(info, target):
|
||||
"""Compatibility check for PPC64 and PPC64LE architectures."""
|
||||
basename = platform.machine()
|
||||
generation_match = re.search(r"POWER(\d+)", info.get("cpu", ""))
|
||||
try:
|
||||
generation = int(generation_match.group(1))
|
||||
except AttributeError:
|
||||
# There might be no match under emulated environments. For instance
|
||||
# emulating a ppc64le with QEMU and Docker still reports the host
|
||||
# /proc/cpuinfo and not a Power
|
||||
generation = 0
|
||||
|
||||
# We can use a target if it descends from our machine type and our
|
||||
# generation (9 for POWER9, etc) is at least its generation.
|
||||
arch_root = TARGETS[basename]
|
||||
arch_root = TARGETS[_machine()]
|
||||
return (
|
||||
target == arch_root or arch_root in target.ancestors
|
||||
) and target.generation <= generation
|
||||
) and target.generation <= info.generation
|
||||
|
||||
|
||||
@compatibility_check(architecture_family="x86_64")
|
||||
@compatibility_check(architecture_family=X86_64)
|
||||
def compatibility_check_for_x86_64(info, target):
|
||||
"""Compatibility check for x86_64 architectures."""
|
||||
basename = "x86_64"
|
||||
vendor = info.get("vendor_id", "generic")
|
||||
features = set(info.get("flags", "").split())
|
||||
|
||||
# We can use a target if it descends from our machine type, is from our
|
||||
# vendor, and we have all of its features
|
||||
arch_root = TARGETS[basename]
|
||||
arch_root = TARGETS[X86_64]
|
||||
return (
|
||||
(target == arch_root or arch_root in target.ancestors)
|
||||
and target.vendor in (vendor, "generic")
|
||||
and target.features.issubset(features)
|
||||
and target.vendor in (info.vendor, "generic")
|
||||
and target.features.issubset(info.features)
|
||||
)
|
||||
|
||||
|
||||
@compatibility_check(architecture_family="aarch64")
|
||||
@compatibility_check(architecture_family=AARCH64)
|
||||
def compatibility_check_for_aarch64(info, target):
|
||||
"""Compatibility check for AARCH64 architectures."""
|
||||
basename = "aarch64"
|
||||
features = set(info.get("Features", "").split())
|
||||
vendor = info.get("CPU implementer", "generic")
|
||||
|
||||
# At the moment it's not clear how to detect compatibility with
|
||||
# At the moment, it's not clear how to detect compatibility with
|
||||
# a specific version of the architecture
|
||||
if target.vendor == "generic" and target.name != "aarch64":
|
||||
if target.vendor == "generic" and target.name != AARCH64:
|
||||
return False
|
||||
|
||||
arch_root = TARGETS[basename]
|
||||
arch_root = TARGETS[AARCH64]
|
||||
arch_root_and_vendor = arch_root == target.family and target.vendor in (
|
||||
vendor,
|
||||
info.vendor,
|
||||
"generic",
|
||||
)
|
||||
|
||||
# On macOS it seems impossible to get all the CPU features
|
||||
# with syctl info, but for ARM we can get the exact model
|
||||
if platform.system() == "Darwin":
|
||||
model_key = info.get("model", basename)
|
||||
model = TARGETS[model_key]
|
||||
model = TARGETS[info.name]
|
||||
return arch_root_and_vendor and (target == model or target in model.ancestors)
|
||||
|
||||
return arch_root_and_vendor and target.features.issubset(features)
|
||||
return arch_root_and_vendor and target.features.issubset(info.features)
|
||||
|
||||
|
||||
@compatibility_check(architecture_family="riscv64")
|
||||
@compatibility_check(architecture_family=RISCV64)
|
||||
def compatibility_check_for_riscv64(info, target):
|
||||
"""Compatibility check for riscv64 architectures."""
|
||||
basename = "riscv64"
|
||||
uarch = info.get("uarch")
|
||||
|
||||
# sifive unmatched board
|
||||
if uarch == "sifive,u74-mc":
|
||||
uarch = "u74mc"
|
||||
# catch-all for unknown uarchs
|
||||
else:
|
||||
uarch = "riscv64"
|
||||
|
||||
arch_root = TARGETS[basename]
|
||||
arch_root = TARGETS[RISCV64]
|
||||
return (target == arch_root or arch_root in target.ancestors) and (
|
||||
target == uarch or target.vendor == "generic"
|
||||
target.name == info.name or target.vendor == "generic"
|
||||
)
|
||||
|
||||
|
||||
def brand_string() -> Optional[str]:
|
||||
"""Returns the brand string of the host, if detected, or None."""
|
||||
if platform.system() == "Darwin":
|
||||
return _check_output(
|
||||
["sysctl", "-n", "machdep.cpu.brand_string"], env=_ensure_bin_usrbin_in_path()
|
||||
).strip()
|
||||
|
||||
if host().family == X86_64:
|
||||
return CpuidInfoCollector().brand_string()
|
||||
|
||||
return None
|
||||
|
@ -2,9 +2,7 @@
|
||||
# Archspec Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
"""Types and functions to manage information
|
||||
on CPU microarchitectures.
|
||||
"""
|
||||
"""Types and functions to manage information on CPU microarchitectures."""
|
||||
import functools
|
||||
import platform
|
||||
import re
|
||||
@ -13,6 +11,7 @@
|
||||
import archspec
|
||||
import archspec.cpu.alias
|
||||
import archspec.cpu.schema
|
||||
|
||||
from .alias import FEATURE_ALIASES
|
||||
from .schema import LazyDictionary
|
||||
|
||||
@ -47,7 +46,7 @@ class Microarchitecture:
|
||||
which has "broadwell" as a parent, supports running binaries
|
||||
optimized for "broadwell".
|
||||
vendor (str): vendor of the micro-architecture
|
||||
features (list of str): supported CPU flags. Note that the semantic
|
||||
features (set of str): supported CPU flags. Note that the semantic
|
||||
of the flags in this field might vary among architectures, if
|
||||
at all present. For instance x86_64 processors will list all
|
||||
the flags supported by a given CPU while Arm processors will
|
||||
@ -64,21 +63,24 @@ class Microarchitecture:
|
||||
passed in as argument above.
|
||||
* versions: versions that support this micro-architecture.
|
||||
|
||||
generation (int): generation of the micro-architecture, if
|
||||
relevant.
|
||||
generation (int): generation of the micro-architecture, if relevant.
|
||||
cpu_part (str): cpu part of the architecture, if relevant.
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
||||
#: Aliases for micro-architecture's features
|
||||
feature_aliases = FEATURE_ALIASES
|
||||
|
||||
def __init__(self, name, parents, vendor, features, compilers, generation=0):
|
||||
def __init__(self, name, parents, vendor, features, compilers, generation=0, cpu_part=""):
|
||||
self.name = name
|
||||
self.parents = parents
|
||||
self.vendor = vendor
|
||||
self.features = features
|
||||
self.compilers = compilers
|
||||
# Only relevant for PowerPC
|
||||
self.generation = generation
|
||||
# Only relevant for AArch64
|
||||
self.cpu_part = cpu_part
|
||||
# Cache the ancestor computation
|
||||
self._ancestors = None
|
||||
|
||||
@ -110,6 +112,7 @@ def __eq__(self, other):
|
||||
and self.parents == other.parents # avoid ancestors here
|
||||
and self.compilers == other.compilers
|
||||
and self.generation == other.generation
|
||||
and self.cpu_part == other.cpu_part
|
||||
)
|
||||
|
||||
@coerce_target_names
|
||||
@ -142,7 +145,8 @@ def __repr__(self):
|
||||
cls_name = self.__class__.__name__
|
||||
fmt = (
|
||||
cls_name + "({0.name!r}, {0.parents!r}, {0.vendor!r}, "
|
||||
"{0.features!r}, {0.compilers!r}, {0.generation!r})"
|
||||
"{0.features!r}, {0.compilers!r}, generation={0.generation!r}, "
|
||||
"cpu_part={0.cpu_part!r})"
|
||||
)
|
||||
return fmt.format(self)
|
||||
|
||||
@ -180,24 +184,30 @@ def generic(self):
|
||||
generics = [x for x in [self] + self.ancestors if x.vendor == "generic"]
|
||||
return max(generics, key=lambda x: len(x.ancestors))
|
||||
|
||||
def to_dict(self, return_list_of_items=False):
|
||||
"""Returns a dictionary representation of this object.
|
||||
def to_dict(self):
|
||||
"""Returns a dictionary representation of this object."""
|
||||
return {
|
||||
"name": str(self.name),
|
||||
"vendor": str(self.vendor),
|
||||
"features": sorted(str(x) for x in self.features),
|
||||
"generation": self.generation,
|
||||
"parents": [str(x) for x in self.parents],
|
||||
"compilers": self.compilers,
|
||||
"cpupart": self.cpu_part,
|
||||
}
|
||||
|
||||
Args:
|
||||
return_list_of_items (bool): if True returns an ordered list of
|
||||
items instead of the dictionary
|
||||
"""
|
||||
list_of_items = [
|
||||
("name", str(self.name)),
|
||||
("vendor", str(self.vendor)),
|
||||
("features", sorted(str(x) for x in self.features)),
|
||||
("generation", self.generation),
|
||||
("parents", [str(x) for x in self.parents]),
|
||||
]
|
||||
if return_list_of_items:
|
||||
return list_of_items
|
||||
|
||||
return dict(list_of_items)
|
||||
@staticmethod
|
||||
def from_dict(data) -> "Microarchitecture":
|
||||
"""Construct a microarchitecture from a dictionary representation."""
|
||||
return Microarchitecture(
|
||||
name=data["name"],
|
||||
parents=[TARGETS[x] for x in data["parents"]],
|
||||
vendor=data["vendor"],
|
||||
features=set(data["features"]),
|
||||
compilers=data.get("compilers", {}),
|
||||
generation=data.get("generation", 0),
|
||||
cpu_part=data.get("cpupart", ""),
|
||||
)
|
||||
|
||||
def optimization_flags(self, compiler, version):
|
||||
"""Returns a string containing the optimization flags that needs
|
||||
@ -271,9 +281,7 @@ def tuplify(ver):
|
||||
flags = flags_fmt.format(**compiler_entry)
|
||||
return flags
|
||||
|
||||
msg = (
|
||||
"cannot produce optimized binary for micro-architecture '{0}' with {1}@{2}"
|
||||
)
|
||||
msg = "cannot produce optimized binary for micro-architecture '{0}' with {1}@{2}"
|
||||
if compiler_info:
|
||||
versions = [x["versions"] for x in compiler_info]
|
||||
msg += f' [supported compiler versions are {", ".join(versions)}]'
|
||||
@ -289,9 +297,7 @@ def generic_microarchitecture(name):
|
||||
Args:
|
||||
name (str): name of the micro-architecture
|
||||
"""
|
||||
return Microarchitecture(
|
||||
name, parents=[], vendor="generic", features=[], compilers={}
|
||||
)
|
||||
return Microarchitecture(name, parents=[], vendor="generic", features=set(), compilers={})
|
||||
|
||||
|
||||
def version_components(version):
|
||||
@ -344,9 +350,10 @@ def fill_target_from_dict(name, data, targets):
|
||||
features = set(values["features"])
|
||||
compilers = values.get("compilers", {})
|
||||
generation = values.get("generation", 0)
|
||||
cpu_part = values.get("cpupart", "")
|
||||
|
||||
targets[name] = Microarchitecture(
|
||||
name, parents, vendor, features, compilers, generation
|
||||
name, parents, vendor, features, compilers, generation=generation, cpu_part=cpu_part
|
||||
)
|
||||
|
||||
known_targets = {}
|
||||
|
68
lib/spack/external/archspec/cpu/schema.py
vendored
68
lib/spack/external/archspec/cpu/schema.py
vendored
@ -7,7 +7,9 @@
|
||||
"""
|
||||
import collections.abc
|
||||
import json
|
||||
import os.path
|
||||
import os
|
||||
import pathlib
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
class LazyDictionary(collections.abc.MutableMapping):
|
||||
@ -46,21 +48,65 @@ def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
|
||||
def _load_json_file(json_file):
|
||||
json_dir = os.path.join(os.path.dirname(__file__), "..", "json", "cpu")
|
||||
json_dir = os.path.abspath(json_dir)
|
||||
#: Environment variable that might point to a directory with a user defined JSON file
|
||||
DIR_FROM_ENVIRONMENT = "ARCHSPEC_CPU_DIR"
|
||||
|
||||
def _factory():
|
||||
filename = os.path.join(json_dir, json_file)
|
||||
with open(filename, "r", encoding="utf-8") as file:
|
||||
return json.load(file)
|
||||
#: Environment variable that might point to a directory with extensions to JSON files
|
||||
EXTENSION_DIR_FROM_ENVIRONMENT = "ARCHSPEC_EXTENSION_CPU_DIR"
|
||||
|
||||
return _factory
|
||||
|
||||
def _json_file(filename: str, allow_custom: bool = False) -> Tuple[pathlib.Path, pathlib.Path]:
|
||||
"""Given a filename, returns the absolute path for the main JSON file, and an
|
||||
optional absolute path for an extension JSON file.
|
||||
|
||||
Args:
|
||||
filename: filename for the JSON file
|
||||
allow_custom: if True, allows overriding the location where the file resides
|
||||
"""
|
||||
json_dir = pathlib.Path(__file__).parent / ".." / "json" / "cpu"
|
||||
if allow_custom and DIR_FROM_ENVIRONMENT in os.environ:
|
||||
json_dir = pathlib.Path(os.environ[DIR_FROM_ENVIRONMENT])
|
||||
json_dir = json_dir.absolute()
|
||||
json_file = json_dir / filename
|
||||
|
||||
extension_file = None
|
||||
if allow_custom and EXTENSION_DIR_FROM_ENVIRONMENT in os.environ:
|
||||
extension_dir = pathlib.Path(os.environ[EXTENSION_DIR_FROM_ENVIRONMENT])
|
||||
extension_dir.absolute()
|
||||
extension_file = extension_dir / filename
|
||||
|
||||
return json_file, extension_file
|
||||
|
||||
|
||||
def _load(json_file: pathlib.Path, extension_file: pathlib.Path):
|
||||
with open(json_file, "r", encoding="utf-8") as file:
|
||||
data = json.load(file)
|
||||
|
||||
if not extension_file or not extension_file.exists():
|
||||
return data
|
||||
|
||||
with open(extension_file, "r", encoding="utf-8") as file:
|
||||
extension_data = json.load(file)
|
||||
|
||||
top_level_sections = list(data.keys())
|
||||
for key in top_level_sections:
|
||||
if key not in extension_data:
|
||||
continue
|
||||
|
||||
data[key].update(extension_data[key])
|
||||
|
||||
return data
|
||||
|
||||
|
||||
#: In memory representation of the data in microarchitectures.json,
|
||||
#: loaded on first access
|
||||
TARGETS_JSON = LazyDictionary(_load_json_file("microarchitectures.json"))
|
||||
TARGETS_JSON = LazyDictionary(_load, *_json_file("microarchitectures.json", allow_custom=True))
|
||||
|
||||
#: JSON schema for microarchitectures.json, loaded on first access
|
||||
SCHEMA = LazyDictionary(_load_json_file("microarchitectures_schema.json"))
|
||||
TARGETS_JSON_SCHEMA = LazyDictionary(_load, *_json_file("microarchitectures_schema.json"))
|
||||
|
||||
#: Information on how to call 'cpuid' to get information on the HOST CPU
|
||||
CPUID_JSON = LazyDictionary(_load, *_json_file("cpuid.json", allow_custom=True))
|
||||
|
||||
#: JSON schema for cpuid.json, loaded on first access
|
||||
CPUID_JSON_SCHEMA = LazyDictionary(_load, *_json_file("cpuid_schema.json"))
|
||||
|
10
lib/spack/external/archspec/json/README.md
vendored
10
lib/spack/external/archspec/json/README.md
vendored
@ -9,11 +9,11 @@ language specific APIs.
|
||||
|
||||
Currently the repository contains the following JSON files:
|
||||
```console
|
||||
.
|
||||
├── COPYRIGHT
|
||||
└── cpu
|
||||
├── microarchitectures.json # Contains information on CPU microarchitectures
|
||||
└── microarchitectures_schema.json # Schema for the file above
|
||||
cpu/
|
||||
├── cpuid.json # Contains information on CPUID calls to retrieve vendor and features on x86_64
|
||||
├── cpuid_schema.json # Schema for the file above
|
||||
├── microarchitectures.json # Contains information on CPU microarchitectures
|
||||
└── microarchitectures_schema.json # Schema for the file above
|
||||
```
|
||||
|
||||
|
||||
|
1050
lib/spack/external/archspec/json/cpu/cpuid.json
vendored
Normal file
1050
lib/spack/external/archspec/json/cpu/cpuid.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
134
lib/spack/external/archspec/json/cpu/cpuid_schema.json
vendored
Normal file
134
lib/spack/external/archspec/json/cpu/cpuid_schema.json
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Schema for microarchitecture definitions and feature aliases",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"vendor": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"input": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"eax": {
|
||||
"type": "integer"
|
||||
},
|
||||
"ecx": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"highest_extension_support": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"input": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"eax": {
|
||||
"type": "integer"
|
||||
},
|
||||
"ecx": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"input": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"eax": {
|
||||
"type": "integer"
|
||||
},
|
||||
"ecx": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bits": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"register": {
|
||||
"type": "string"
|
||||
},
|
||||
"bit": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"extension-flags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"input": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"eax": {
|
||||
"type": "integer"
|
||||
},
|
||||
"ecx": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bits": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"register": {
|
||||
"type": "string"
|
||||
},
|
||||
"bit": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2225,10 +2225,14 @@
|
||||
],
|
||||
"nvhpc": [
|
||||
{
|
||||
"versions": "21.11:",
|
||||
"versions": "21.11:23.8",
|
||||
"name": "zen3",
|
||||
"flags": "-tp {name}",
|
||||
"warnings": "zen4 is not fully supported by nvhpc yet, falling back to zen3"
|
||||
"warnings": "zen4 is not fully supported by nvhpc versions < 23.9, falling back to zen3"
|
||||
},
|
||||
{
|
||||
"versions": "23.9:",
|
||||
"flags": "-tp {name}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -2711,7 +2715,8 @@
|
||||
"flags": "-mcpu=thunderx2t99"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cpupart": "0x0af"
|
||||
},
|
||||
"a64fx": {
|
||||
"from": ["armv8.2a"],
|
||||
@ -2779,7 +2784,8 @@
|
||||
"flags": "-march=armv8.2-a+crc+crypto+fp16+sve"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cpupart": "0x001"
|
||||
},
|
||||
"cortex_a72": {
|
||||
"from": ["aarch64"],
|
||||
@ -2816,7 +2822,8 @@
|
||||
"flags" : "-mcpu=cortex-a72"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cpupart": "0xd08"
|
||||
},
|
||||
"neoverse_n1": {
|
||||
"from": ["cortex_a72", "armv8.2a"],
|
||||
@ -2837,8 +2844,7 @@
|
||||
"asimdrdm",
|
||||
"lrcpc",
|
||||
"dcpop",
|
||||
"asimddp",
|
||||
"ssbs"
|
||||
"asimddp"
|
||||
],
|
||||
"compilers" : {
|
||||
"gcc": [
|
||||
@ -2902,7 +2908,8 @@
|
||||
"flags": "-tp {name}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cpupart": "0xd0c"
|
||||
},
|
||||
"neoverse_v1": {
|
||||
"from": ["neoverse_n1", "armv8.4a"],
|
||||
@ -2926,8 +2933,6 @@
|
||||
"lrcpc",
|
||||
"dcpop",
|
||||
"sha3",
|
||||
"sm3",
|
||||
"sm4",
|
||||
"asimddp",
|
||||
"sha512",
|
||||
"sve",
|
||||
@ -2936,9 +2941,6 @@
|
||||
"uscat",
|
||||
"ilrcpc",
|
||||
"flagm",
|
||||
"ssbs",
|
||||
"paca",
|
||||
"pacg",
|
||||
"dcpodp",
|
||||
"svei8mm",
|
||||
"svebf16",
|
||||
@ -3006,7 +3008,7 @@
|
||||
},
|
||||
{
|
||||
"versions": "11:",
|
||||
"flags" : "-march=armv8.4-a+sve+ssbs+fp16+bf16+crypto+i8mm+rng"
|
||||
"flags" : "-march=armv8.4-a+sve+fp16+bf16+crypto+i8mm+rng"
|
||||
},
|
||||
{
|
||||
"versions": "12:",
|
||||
@ -3030,7 +3032,8 @@
|
||||
"flags": "-tp {name}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cpupart": "0xd40"
|
||||
},
|
||||
"neoverse_v2": {
|
||||
"from": ["neoverse_n1", "armv9.0a"],
|
||||
@ -3054,35 +3057,22 @@
|
||||
"lrcpc",
|
||||
"dcpop",
|
||||
"sha3",
|
||||
"sm3",
|
||||
"sm4",
|
||||
"asimddp",
|
||||
"sha512",
|
||||
"sve",
|
||||
"asimdfhm",
|
||||
"dit",
|
||||
"uscat",
|
||||
"ilrcpc",
|
||||
"flagm",
|
||||
"ssbs",
|
||||
"sb",
|
||||
"paca",
|
||||
"pacg",
|
||||
"dcpodp",
|
||||
"sve2",
|
||||
"sveaes",
|
||||
"svepmull",
|
||||
"svebitperm",
|
||||
"svesha3",
|
||||
"svesm4",
|
||||
"flagm2",
|
||||
"frint",
|
||||
"svei8mm",
|
||||
"svebf16",
|
||||
"i8mm",
|
||||
"bf16",
|
||||
"dgh",
|
||||
"bti"
|
||||
"bf16"
|
||||
],
|
||||
"compilers" : {
|
||||
"gcc": [
|
||||
@ -3107,15 +3097,19 @@
|
||||
"flags" : "-march=armv8.5-a+sve -mtune=cortex-a76"
|
||||
},
|
||||
{
|
||||
"versions": "10.0:11.99",
|
||||
"versions": "10.0:11.3.99",
|
||||
"flags" : "-march=armv8.5-a+sve+sve2+i8mm+bf16 -mtune=cortex-a77"
|
||||
},
|
||||
{
|
||||
"versions": "11.4:11.99",
|
||||
"flags" : "-mcpu=neoverse-v2"
|
||||
},
|
||||
{
|
||||
"versions": "12.0:12.99",
|
||||
"versions": "12.0:12.2.99",
|
||||
"flags" : "-march=armv9-a+i8mm+bf16 -mtune=cortex-a710"
|
||||
},
|
||||
{
|
||||
"versions": "13.0:",
|
||||
"versions": "12.3:",
|
||||
"flags" : "-mcpu=neoverse-v2"
|
||||
}
|
||||
],
|
||||
@ -3150,7 +3144,112 @@
|
||||
"flags": "-tp {name}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cpupart": "0xd4f"
|
||||
},
|
||||
"neoverse_n2": {
|
||||
"from": ["neoverse_n1", "armv9.0a"],
|
||||
"vendor": "ARM",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"aes",
|
||||
"pmull",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"crc32",
|
||||
"atomics",
|
||||
"fphp",
|
||||
"asimdhp",
|
||||
"cpuid",
|
||||
"asimdrdm",
|
||||
"jscvt",
|
||||
"fcma",
|
||||
"lrcpc",
|
||||
"dcpop",
|
||||
"sha3",
|
||||
"asimddp",
|
||||
"sha512",
|
||||
"sve",
|
||||
"asimdfhm",
|
||||
"uscat",
|
||||
"ilrcpc",
|
||||
"flagm",
|
||||
"sb",
|
||||
"dcpodp",
|
||||
"sve2",
|
||||
"flagm2",
|
||||
"frint",
|
||||
"svei8mm",
|
||||
"svebf16",
|
||||
"i8mm",
|
||||
"bf16"
|
||||
],
|
||||
"compilers" : {
|
||||
"gcc": [
|
||||
{
|
||||
"versions": "4.8:5.99",
|
||||
"flags": "-march=armv8-a"
|
||||
},
|
||||
{
|
||||
"versions": "6:6.99",
|
||||
"flags" : "-march=armv8.1-a"
|
||||
},
|
||||
{
|
||||
"versions": "7.0:7.99",
|
||||
"flags" : "-march=armv8.2-a -mtune=cortex-a72"
|
||||
},
|
||||
{
|
||||
"versions": "8.0:8.99",
|
||||
"flags" : "-march=armv8.4-a+sve -mtune=cortex-a72"
|
||||
},
|
||||
{
|
||||
"versions": "9.0:9.99",
|
||||
"flags" : "-march=armv8.5-a+sve -mtune=cortex-a76"
|
||||
},
|
||||
{
|
||||
"versions": "10.0:10.99",
|
||||
"flags" : "-march=armv8.5-a+sve+sve2+i8mm+bf16 -mtune=cortex-a77"
|
||||
},
|
||||
{
|
||||
"versions": "11.0:",
|
||||
"flags" : "-mcpu=neoverse-n2"
|
||||
}
|
||||
],
|
||||
"clang" : [
|
||||
{
|
||||
"versions": "9.0:10.99",
|
||||
"flags" : "-march=armv8.5-a+sve"
|
||||
},
|
||||
{
|
||||
"versions": "11.0:13.99",
|
||||
"flags" : "-march=armv8.5-a+sve+sve2+i8mm+bf16"
|
||||
},
|
||||
{
|
||||
"versions": "14.0:15.99",
|
||||
"flags" : "-march=armv9-a+i8mm+bf16"
|
||||
},
|
||||
{
|
||||
"versions": "16.0:",
|
||||
"flags" : "-mcpu=neoverse-n2"
|
||||
}
|
||||
],
|
||||
"arm" : [
|
||||
{
|
||||
"versions": "23.04.0:",
|
||||
"flags" : "-mcpu=neoverse-n2"
|
||||
}
|
||||
],
|
||||
"nvhpc" : [
|
||||
{
|
||||
"versions": "23.3:",
|
||||
"name": "neoverse-n1",
|
||||
"flags": "-tp {name}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cpupart": "0xd49"
|
||||
},
|
||||
"m1": {
|
||||
"from": ["armv8.4a"],
|
||||
@ -3216,7 +3315,8 @@
|
||||
"flags" : "-mcpu=apple-m1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cpupart": "0x022"
|
||||
},
|
||||
"m2": {
|
||||
"from": ["m1", "armv8.5a"],
|
||||
@ -3294,7 +3394,8 @@
|
||||
"flags" : "-mcpu=apple-m2"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cpupart": "0x032"
|
||||
},
|
||||
"arm": {
|
||||
"from": [],
|
||||
|
@ -52,6 +52,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cpupart": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@ -107,4 +110,4 @@
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
lib/spack/external/archspec/vendor/cpuid/LICENSE
vendored
Normal file
20
lib/spack/external/archspec/vendor/cpuid/LICENSE
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Anders Høst
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
76
lib/spack/external/archspec/vendor/cpuid/README.md
vendored
Normal file
76
lib/spack/external/archspec/vendor/cpuid/README.md
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
cpuid.py
|
||||
========
|
||||
|
||||
Now, this is silly!
|
||||
|
||||
Pure Python library for accessing information about x86 processors
|
||||
by querying the [CPUID](http://en.wikipedia.org/wiki/CPUID)
|
||||
instruction. Well, not exactly pure Python...
|
||||
|
||||
It works by allocating a small piece of virtual memory, copying
|
||||
a raw x86 function to that memory, giving the memory execute
|
||||
permissions and then calling the memory as a function. The injected
|
||||
function executes the CPUID instruction and copies the result back
|
||||
to a ctypes.Structure where is can be read by Python.
|
||||
|
||||
It should work fine on both 32 and 64 bit versions of Windows and Linux
|
||||
running x86 processors. Apple OS X and other BSD systems should also work,
|
||||
not tested though...
|
||||
|
||||
|
||||
Why?
|
||||
----
|
||||
For poops and giggles. Plus, having access to a low-level feature
|
||||
without having to compile a C wrapper is pretty neat.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
Getting info with eax=0:
|
||||
|
||||
import cpuid
|
||||
|
||||
q = cpuid.CPUID()
|
||||
eax, ebx, ecx, edx = q(0)
|
||||
|
||||
Running the files:
|
||||
|
||||
$ python example.py
|
||||
Vendor ID : GenuineIntel
|
||||
CPU name : Intel(R) Xeon(R) CPU W3550 @ 3.07GHz
|
||||
|
||||
Vector instructions supported:
|
||||
SSE : Yes
|
||||
SSE2 : Yes
|
||||
SSE3 : Yes
|
||||
SSSE3 : Yes
|
||||
SSE4.1 : Yes
|
||||
SSE4.2 : Yes
|
||||
SSE4a : --
|
||||
AVX : --
|
||||
AVX2 : --
|
||||
|
||||
$ python cpuid.py
|
||||
CPUID A B C D
|
||||
00000000 0000000b 756e6547 6c65746e 49656e69
|
||||
00000001 000106a5 00100800 009ce3bd bfebfbff
|
||||
00000002 55035a01 00f0b2e4 00000000 09ca212c
|
||||
00000003 00000000 00000000 00000000 00000000
|
||||
00000004 00000000 00000000 00000000 00000000
|
||||
00000005 00000040 00000040 00000003 00001120
|
||||
00000006 00000003 00000002 00000001 00000000
|
||||
00000007 00000000 00000000 00000000 00000000
|
||||
00000008 00000000 00000000 00000000 00000000
|
||||
00000009 00000000 00000000 00000000 00000000
|
||||
0000000a 07300403 00000044 00000000 00000603
|
||||
0000000b 00000000 00000000 00000095 00000000
|
||||
80000000 80000008 00000000 00000000 00000000
|
||||
80000001 00000000 00000000 00000001 28100800
|
||||
80000002 65746e49 2952286c 6f655820 2952286e
|
||||
80000003 55504320 20202020 20202020 57202020
|
||||
80000004 30353533 20402020 37302e33 007a4847
|
||||
80000005 00000000 00000000 00000000 00000000
|
||||
80000006 00000000 00000000 01006040 00000000
|
||||
80000007 00000000 00000000 00000000 00000100
|
||||
80000008 00003024 00000000 00000000 00000000
|
||||
|
172
lib/spack/external/archspec/vendor/cpuid/cpuid.py
vendored
Normal file
172
lib/spack/external/archspec/vendor/cpuid/cpuid.py
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2024 Anders Høst
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import platform
|
||||
import os
|
||||
import ctypes
|
||||
from ctypes import c_uint32, c_long, c_ulong, c_size_t, c_void_p, POINTER, CFUNCTYPE
|
||||
|
||||
# Posix x86_64:
|
||||
# Three first call registers : RDI, RSI, RDX
|
||||
# Volatile registers : RAX, RCX, RDX, RSI, RDI, R8-11
|
||||
|
||||
# Windows x86_64:
|
||||
# Three first call registers : RCX, RDX, R8
|
||||
# Volatile registers : RAX, RCX, RDX, R8-11
|
||||
|
||||
# cdecl 32 bit:
|
||||
# Three first call registers : Stack (%esp)
|
||||
# Volatile registers : EAX, ECX, EDX
|
||||
|
||||
_POSIX_64_OPC = [
|
||||
0x53, # push %rbx
|
||||
0x89, 0xf0, # mov %esi,%eax
|
||||
0x89, 0xd1, # mov %edx,%ecx
|
||||
0x0f, 0xa2, # cpuid
|
||||
0x89, 0x07, # mov %eax,(%rdi)
|
||||
0x89, 0x5f, 0x04, # mov %ebx,0x4(%rdi)
|
||||
0x89, 0x4f, 0x08, # mov %ecx,0x8(%rdi)
|
||||
0x89, 0x57, 0x0c, # mov %edx,0xc(%rdi)
|
||||
0x5b, # pop %rbx
|
||||
0xc3 # retq
|
||||
]
|
||||
|
||||
_WINDOWS_64_OPC = [
|
||||
0x53, # push %rbx
|
||||
0x89, 0xd0, # mov %edx,%eax
|
||||
0x49, 0x89, 0xc9, # mov %rcx,%r9
|
||||
0x44, 0x89, 0xc1, # mov %r8d,%ecx
|
||||
0x0f, 0xa2, # cpuid
|
||||
0x41, 0x89, 0x01, # mov %eax,(%r9)
|
||||
0x41, 0x89, 0x59, 0x04, # mov %ebx,0x4(%r9)
|
||||
0x41, 0x89, 0x49, 0x08, # mov %ecx,0x8(%r9)
|
||||
0x41, 0x89, 0x51, 0x0c, # mov %edx,0xc(%r9)
|
||||
0x5b, # pop %rbx
|
||||
0xc3 # retq
|
||||
]
|
||||
|
||||
_CDECL_32_OPC = [
|
||||
0x53, # push %ebx
|
||||
0x57, # push %edi
|
||||
0x8b, 0x7c, 0x24, 0x0c, # mov 0xc(%esp),%edi
|
||||
0x8b, 0x44, 0x24, 0x10, # mov 0x10(%esp),%eax
|
||||
0x8b, 0x4c, 0x24, 0x14, # mov 0x14(%esp),%ecx
|
||||
0x0f, 0xa2, # cpuid
|
||||
0x89, 0x07, # mov %eax,(%edi)
|
||||
0x89, 0x5f, 0x04, # mov %ebx,0x4(%edi)
|
||||
0x89, 0x4f, 0x08, # mov %ecx,0x8(%edi)
|
||||
0x89, 0x57, 0x0c, # mov %edx,0xc(%edi)
|
||||
0x5f, # pop %edi
|
||||
0x5b, # pop %ebx
|
||||
0xc3 # ret
|
||||
]
|
||||
|
||||
is_windows = os.name == "nt"
|
||||
is_64bit = ctypes.sizeof(ctypes.c_voidp) == 8
|
||||
|
||||
|
||||
class CPUID_struct(ctypes.Structure):
|
||||
_register_names = ("eax", "ebx", "ecx", "edx")
|
||||
_fields_ = [(r, c_uint32) for r in _register_names]
|
||||
|
||||
def __getitem__(self, item):
|
||||
if item not in self._register_names:
|
||||
raise KeyError(item)
|
||||
return getattr(self, item)
|
||||
|
||||
def __repr__(self):
|
||||
return "eax=0x{:x}, ebx=0x{:x}, ecx=0x{:x}, edx=0x{:x}".format(self.eax, self.ebx, self.ecx, self.edx)
|
||||
|
||||
|
||||
class CPUID(object):
|
||||
def __init__(self):
|
||||
if platform.machine() not in ("AMD64", "x86_64", "x86", "i686"):
|
||||
raise SystemError("Only available for x86")
|
||||
|
||||
if is_windows:
|
||||
if is_64bit:
|
||||
# VirtualAlloc seems to fail under some weird
|
||||
# circumstances when ctypes.windll.kernel32 is
|
||||
# used under 64 bit Python. CDLL fixes this.
|
||||
self.win = ctypes.CDLL("kernel32.dll")
|
||||
opc = _WINDOWS_64_OPC
|
||||
else:
|
||||
# Here ctypes.windll.kernel32 is needed to get the
|
||||
# right DLL. Otherwise it will fail when running
|
||||
# 32 bit Python on 64 bit Windows.
|
||||
self.win = ctypes.windll.kernel32
|
||||
opc = _CDECL_32_OPC
|
||||
else:
|
||||
opc = _POSIX_64_OPC if is_64bit else _CDECL_32_OPC
|
||||
|
||||
size = len(opc)
|
||||
code = (ctypes.c_ubyte * size)(*opc)
|
||||
|
||||
if is_windows:
|
||||
self.win.VirtualAlloc.restype = c_void_p
|
||||
self.win.VirtualAlloc.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong, ctypes.c_ulong]
|
||||
self.addr = self.win.VirtualAlloc(None, size, 0x1000, 0x40)
|
||||
if not self.addr:
|
||||
raise MemoryError("Could not allocate RWX memory")
|
||||
ctypes.memmove(self.addr, code, size)
|
||||
else:
|
||||
from mmap import (
|
||||
mmap,
|
||||
MAP_PRIVATE,
|
||||
MAP_ANONYMOUS,
|
||||
PROT_WRITE,
|
||||
PROT_READ,
|
||||
PROT_EXEC,
|
||||
)
|
||||
self.mm = mmap(
|
||||
-1,
|
||||
size,
|
||||
flags=MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
prot=PROT_WRITE | PROT_READ | PROT_EXEC,
|
||||
)
|
||||
self.mm.write(code)
|
||||
self.addr = ctypes.addressof(ctypes.c_int.from_buffer(self.mm))
|
||||
|
||||
func_type = CFUNCTYPE(None, POINTER(CPUID_struct), c_uint32, c_uint32)
|
||||
self.func_ptr = func_type(self.addr)
|
||||
|
||||
def __call__(self, eax, ecx=0):
|
||||
struct = self.registers_for(eax=eax, ecx=ecx)
|
||||
return struct.eax, struct.ebx, struct.ecx, struct.edx
|
||||
|
||||
def registers_for(self, eax, ecx=0):
|
||||
"""Calls cpuid with eax and ecx set as the input arguments, and returns a structure
|
||||
containing eax, ebx, ecx, and edx.
|
||||
"""
|
||||
struct = CPUID_struct()
|
||||
self.func_ptr(struct, eax, ecx)
|
||||
return struct
|
||||
|
||||
def __del__(self):
|
||||
if is_windows:
|
||||
self.win.VirtualFree.restype = c_long
|
||||
self.win.VirtualFree.argtypes = [c_void_p, c_size_t, c_ulong]
|
||||
self.win.VirtualFree(self.addr, 0, 0x8000)
|
||||
else:
|
||||
self.mm.close()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def valid_inputs():
|
||||
cpuid = CPUID()
|
||||
for eax in (0x0, 0x80000000):
|
||||
highest, _, _, _ = cpuid(eax)
|
||||
while eax <= highest:
|
||||
regs = cpuid(eax)
|
||||
yield (eax, regs)
|
||||
eax += 1
|
||||
|
||||
|
||||
print(" ".join(x.ljust(8) for x in ("CPUID", "A", "B", "C", "D")).strip())
|
||||
for eax, regs in valid_inputs():
|
||||
print("%08x" % eax, " ".join("%08x" % reg for reg in regs))
|
62
lib/spack/external/archspec/vendor/cpuid/example.py
vendored
Normal file
62
lib/spack/external/archspec/vendor/cpuid/example.py
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2024 Anders Høst
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import struct
|
||||
import cpuid
|
||||
|
||||
|
||||
def cpu_vendor(cpu):
|
||||
_, b, c, d = cpu(0)
|
||||
return struct.pack("III", b, d, c).decode("utf-8")
|
||||
|
||||
|
||||
def cpu_name(cpu):
|
||||
name = "".join((struct.pack("IIII", *cpu(0x80000000 + i)).decode("utf-8")
|
||||
for i in range(2, 5)))
|
||||
|
||||
return name.split('\x00', 1)[0]
|
||||
|
||||
|
||||
def is_set(cpu, leaf, subleaf, reg_idx, bit):
|
||||
"""
|
||||
@param {leaf} %eax
|
||||
@param {sublead} %ecx, 0 in most cases
|
||||
@param {reg_idx} idx of [%eax, %ebx, %ecx, %edx], 0-based
|
||||
@param {bit} bit of reg selected by {reg_idx}, 0-based
|
||||
"""
|
||||
|
||||
regs = cpu(leaf, subleaf)
|
||||
|
||||
if (1 << bit) & regs[reg_idx]:
|
||||
return "Yes"
|
||||
else:
|
||||
return "--"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cpu = cpuid.CPUID()
|
||||
|
||||
print("Vendor ID : %s" % cpu_vendor(cpu))
|
||||
print("CPU name : %s" % cpu_name(cpu))
|
||||
print()
|
||||
print("Vector instructions supported:")
|
||||
print("SSE : %s" % is_set(cpu, 1, 0, 3, 25))
|
||||
print("SSE2 : %s" % is_set(cpu, 1, 0, 3, 26))
|
||||
print("SSE3 : %s" % is_set(cpu, 1, 0, 2, 0))
|
||||
print("SSSE3 : %s" % is_set(cpu, 1, 0, 2, 9))
|
||||
print("SSE4.1 : %s" % is_set(cpu, 1, 0, 2, 19))
|
||||
print("SSE4.2 : %s" % is_set(cpu, 1, 0, 2, 20))
|
||||
print("SSE4a : %s" % is_set(cpu, 0x80000001, 0, 2, 6))
|
||||
print("AVX : %s" % is_set(cpu, 1, 0, 2, 28))
|
||||
print("AVX2 : %s" % is_set(cpu, 7, 0, 1, 5))
|
||||
print("BMI1 : %s" % is_set(cpu, 7, 0, 1, 3))
|
||||
print("BMI2 : %s" % is_set(cpu, 7, 0, 1, 8))
|
||||
# Intel RDT CMT/MBM
|
||||
print("L3 Monitoring : %s" % is_set(cpu, 0xf, 0, 3, 1))
|
||||
print("L3 Occupancy : %s" % is_set(cpu, 0xf, 1, 3, 0))
|
||||
print("L3 Total BW : %s" % is_set(cpu, 0xf, 1, 3, 1))
|
||||
print("L3 Local BW : %s" % is_set(cpu, 0xf, 1, 3, 2))
|
@ -4,7 +4,7 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
#: PEP440 canonical <major>.<minor>.<micro>.<devN> string
|
||||
__version__ = "0.21.0.dev0"
|
||||
__version__ = "0.21.3"
|
||||
spack_version = __version__
|
||||
|
||||
|
||||
|
@ -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",
|
||||
|
@ -66,8 +66,10 @@
|
||||
from spack.stage import Stage
|
||||
from spack.util.executable import which
|
||||
|
||||
_build_cache_relative_path = "build_cache"
|
||||
_build_cache_keys_relative_path = "_pgp"
|
||||
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):
|
||||
@ -481,7 +483,7 @@ def _fetch_and_cache_index(self, mirror_url, cache_entry={}):
|
||||
scheme = urllib.parse.urlparse(mirror_url).scheme
|
||||
|
||||
if scheme != "oci" and not web_util.url_exists(
|
||||
url_util.join(mirror_url, _build_cache_relative_path, "index.json")
|
||||
url_util.join(mirror_url, BUILD_CACHE_RELATIVE_PATH, "index.json")
|
||||
):
|
||||
return False
|
||||
|
||||
@ -600,6 +602,10 @@ def __init__(self, msg):
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
class InvalidMetadataFile(spack.error.SpackError):
|
||||
pass
|
||||
|
||||
|
||||
class UnsignedPackageException(spack.error.SpackError):
|
||||
"""
|
||||
Raised if installation of unsigned package is attempted without
|
||||
@ -614,11 +620,11 @@ def compute_hash(data):
|
||||
|
||||
|
||||
def build_cache_relative_path():
|
||||
return _build_cache_relative_path
|
||||
return BUILD_CACHE_RELATIVE_PATH
|
||||
|
||||
|
||||
def build_cache_keys_relative_path():
|
||||
return _build_cache_keys_relative_path
|
||||
return BUILD_CACHE_KEYS_RELATIVE_PATH
|
||||
|
||||
|
||||
def build_cache_prefix(prefix):
|
||||
@ -1401,7 +1407,7 @@ def _build_tarball_in_stage_dir(spec: Spec, out_url: str, stage_dir: str, option
|
||||
spec_dict = sjson.load(content)
|
||||
else:
|
||||
raise ValueError("{0} not a valid spec file type".format(spec_file))
|
||||
spec_dict["buildcache_layout_version"] = 1
|
||||
spec_dict["buildcache_layout_version"] = CURRENT_BUILD_CACHE_LAYOUT_VERSION
|
||||
spec_dict["binary_cache_checksum"] = {"hash_algorithm": "sha256", "hash": checksum}
|
||||
|
||||
with open(specfile_path, "w") as outfile:
|
||||
@ -1560,6 +1566,42 @@ def _delete_staged_downloads(download_result):
|
||||
download_result["specfile_stage"].destroy()
|
||||
|
||||
|
||||
def _get_valid_spec_file(path: str, max_supported_layout: int) -> Tuple[Dict, int]:
|
||||
"""Read and validate a spec file, returning the spec dict with its layout version, or raising
|
||||
InvalidMetadataFile if invalid."""
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
binary_content = f.read()
|
||||
except OSError:
|
||||
raise InvalidMetadataFile(f"No such file: {path}")
|
||||
|
||||
# In the future we may support transparently decompressing compressed spec files.
|
||||
if binary_content[:2] == b"\x1f\x8b":
|
||||
raise InvalidMetadataFile("Compressed spec files are not supported")
|
||||
|
||||
try:
|
||||
as_string = binary_content.decode("utf-8")
|
||||
if path.endswith(".json.sig"):
|
||||
spec_dict = Spec.extract_json_from_clearsig(as_string)
|
||||
else:
|
||||
spec_dict = json.loads(as_string)
|
||||
except Exception as e:
|
||||
raise InvalidMetadataFile(f"Could not parse {path} due to: {e}") from e
|
||||
|
||||
# Ensure this version is not too new.
|
||||
try:
|
||||
layout_version = int(spec_dict.get("buildcache_layout_version", 0))
|
||||
except ValueError as e:
|
||||
raise InvalidMetadataFile("Could not parse layout version") from e
|
||||
|
||||
if layout_version > max_supported_layout:
|
||||
raise InvalidMetadataFile(
|
||||
f"Layout version {layout_version} is too new for this version of Spack"
|
||||
)
|
||||
|
||||
return spec_dict, layout_version
|
||||
|
||||
|
||||
def download_tarball(spec, unsigned=False, mirrors_for_spec=None):
|
||||
"""
|
||||
Download binary tarball for given package into stage area, returning
|
||||
@ -1652,6 +1694,18 @@ def download_tarball(spec, unsigned=False, mirrors_for_spec=None):
|
||||
try:
|
||||
local_specfile_stage.fetch()
|
||||
local_specfile_stage.check()
|
||||
try:
|
||||
_get_valid_spec_file(
|
||||
local_specfile_stage.save_filename,
|
||||
FORWARD_COMPAT_BUILD_CACHE_LAYOUT_VERSION,
|
||||
)
|
||||
except InvalidMetadataFile as e:
|
||||
tty.warn(
|
||||
f"Ignoring binary package for {spec.name}/{spec.dag_hash()[:7]} "
|
||||
f"from {mirror} due to invalid metadata file: {e}"
|
||||
)
|
||||
local_specfile_stage.destroy()
|
||||
continue
|
||||
except Exception:
|
||||
continue
|
||||
local_specfile_stage.cache_local()
|
||||
@ -1674,14 +1728,26 @@ def download_tarball(spec, unsigned=False, mirrors_for_spec=None):
|
||||
|
||||
else:
|
||||
ext = "json.sig" if try_signed else "json"
|
||||
specfile_path = url_util.join(mirror, _build_cache_relative_path, specfile_prefix)
|
||||
specfile_path = url_util.join(mirror, BUILD_CACHE_RELATIVE_PATH, specfile_prefix)
|
||||
specfile_url = f"{specfile_path}.{ext}"
|
||||
spackfile_url = url_util.join(mirror, _build_cache_relative_path, tarball)
|
||||
spackfile_url = url_util.join(mirror, BUILD_CACHE_RELATIVE_PATH, tarball)
|
||||
local_specfile_stage = try_fetch(specfile_url)
|
||||
if local_specfile_stage:
|
||||
local_specfile_path = local_specfile_stage.save_filename
|
||||
signature_verified = False
|
||||
|
||||
try:
|
||||
_get_valid_spec_file(
|
||||
local_specfile_path, FORWARD_COMPAT_BUILD_CACHE_LAYOUT_VERSION
|
||||
)
|
||||
except InvalidMetadataFile as e:
|
||||
tty.warn(
|
||||
f"Ignoring binary package for {spec.name}/{spec.dag_hash()[:7]} "
|
||||
f"from {mirror} due to invalid metadata file: {e}"
|
||||
)
|
||||
local_specfile_stage.destroy()
|
||||
continue
|
||||
|
||||
if try_signed and not unsigned:
|
||||
# If we found a signed specfile at the root, try to verify
|
||||
# the signature immediately. We will not download the
|
||||
@ -1961,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.
|
||||
@ -1973,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):
|
||||
@ -2001,24 +2070,16 @@ def extract_tarball(spec, download_result, unsigned=False, force=False, timer=ti
|
||||
)
|
||||
|
||||
specfile_path = download_result["specfile_stage"].save_filename
|
||||
|
||||
with open(specfile_path, "r") as inputfile:
|
||||
content = inputfile.read()
|
||||
if specfile_path.endswith(".json.sig"):
|
||||
spec_dict = Spec.extract_json_from_clearsig(content)
|
||||
else:
|
||||
spec_dict = sjson.load(content)
|
||||
|
||||
spec_dict, layout_version = _get_valid_spec_file(
|
||||
specfile_path, FORWARD_COMPAT_BUILD_CACHE_LAYOUT_VERSION
|
||||
)
|
||||
bchecksum = spec_dict["binary_cache_checksum"]
|
||||
|
||||
filename = download_result["tarball_stage"].save_filename
|
||||
signature_verified = download_result["signature_verified"]
|
||||
tmpdir = None
|
||||
|
||||
if (
|
||||
"buildcache_layout_version" not in spec_dict
|
||||
or int(spec_dict["buildcache_layout_version"]) < 1
|
||||
):
|
||||
if layout_version == 0:
|
||||
# Handle the older buildcache layout where the .spack file
|
||||
# contains a spec json, maybe an .asc file (signature),
|
||||
# and another tarball containing the actual install tree.
|
||||
@ -2029,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
|
||||
else:
|
||||
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
|
||||
@ -2053,12 +2114,13 @@ def extract_tarball(spec, download_result, unsigned=False, force=False, timer=ti
|
||||
raise NoChecksumException(
|
||||
tarfile_path, size, contents, "sha256", expected, local_checksum
|
||||
)
|
||||
|
||||
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)
|
||||
@ -2093,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):
|
||||
@ -2184,10 +2273,10 @@ def try_direct_fetch(spec, mirrors=None):
|
||||
|
||||
for mirror in binary_mirrors:
|
||||
buildcache_fetch_url_json = url_util.join(
|
||||
mirror.fetch_url, _build_cache_relative_path, specfile_name
|
||||
mirror.fetch_url, BUILD_CACHE_RELATIVE_PATH, specfile_name
|
||||
)
|
||||
buildcache_fetch_url_signed_json = url_util.join(
|
||||
mirror.fetch_url, _build_cache_relative_path, signed_specfile_name
|
||||
mirror.fetch_url, BUILD_CACHE_RELATIVE_PATH, signed_specfile_name
|
||||
)
|
||||
try:
|
||||
_, _, fs = web_util.read_from_url(buildcache_fetch_url_signed_json)
|
||||
@ -2291,8 +2380,11 @@ def get_keys(install=False, trust=False, force=False, mirrors=None):
|
||||
|
||||
for mirror in mirror_collection.values():
|
||||
fetch_url = mirror.fetch_url
|
||||
# TODO: oci:// does not support signing.
|
||||
if fetch_url.startswith("oci://"):
|
||||
continue
|
||||
keys_url = url_util.join(
|
||||
fetch_url, _build_cache_relative_path, _build_cache_keys_relative_path
|
||||
fetch_url, BUILD_CACHE_RELATIVE_PATH, BUILD_CACHE_KEYS_RELATIVE_PATH
|
||||
)
|
||||
keys_index = url_util.join(keys_url, "index.json")
|
||||
|
||||
@ -2357,7 +2449,7 @@ def push_keys(*mirrors, **kwargs):
|
||||
for mirror in mirrors:
|
||||
push_url = getattr(mirror, "push_url", mirror)
|
||||
keys_url = url_util.join(
|
||||
push_url, _build_cache_relative_path, _build_cache_keys_relative_path
|
||||
push_url, BUILD_CACHE_RELATIVE_PATH, BUILD_CACHE_KEYS_RELATIVE_PATH
|
||||
)
|
||||
keys_local = url_util.local_file_path(keys_url)
|
||||
|
||||
@ -2495,11 +2587,11 @@ def download_buildcache_entry(file_descriptions, mirror_url=None):
|
||||
)
|
||||
|
||||
if mirror_url:
|
||||
mirror_root = os.path.join(mirror_url, _build_cache_relative_path)
|
||||
mirror_root = os.path.join(mirror_url, BUILD_CACHE_RELATIVE_PATH)
|
||||
return _download_buildcache_entry(mirror_root, file_descriptions)
|
||||
|
||||
for mirror in spack.mirror.MirrorCollection(binary=True).values():
|
||||
mirror_root = os.path.join(mirror.fetch_url, _build_cache_relative_path)
|
||||
mirror_root = os.path.join(mirror.fetch_url, BUILD_CACHE_RELATIVE_PATH)
|
||||
|
||||
if _download_buildcache_entry(mirror_root, file_descriptions):
|
||||
return True
|
||||
@ -2590,7 +2682,7 @@ def __init__(self, url, local_hash, urlopen=web_util.urlopen):
|
||||
|
||||
def get_remote_hash(self):
|
||||
# Failure to fetch index.json.hash is not fatal
|
||||
url_index_hash = url_util.join(self.url, _build_cache_relative_path, "index.json.hash")
|
||||
url_index_hash = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, "index.json.hash")
|
||||
try:
|
||||
response = self.urlopen(urllib.request.Request(url_index_hash, headers=self.headers))
|
||||
except urllib.error.URLError:
|
||||
@ -2611,7 +2703,7 @@ def conditional_fetch(self) -> FetchIndexResult:
|
||||
return FetchIndexResult(etag=None, hash=None, data=None, fresh=True)
|
||||
|
||||
# Otherwise, download index.json
|
||||
url_index = url_util.join(self.url, _build_cache_relative_path, "index.json")
|
||||
url_index = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, "index.json")
|
||||
|
||||
try:
|
||||
response = self.urlopen(urllib.request.Request(url_index, headers=self.headers))
|
||||
@ -2655,7 +2747,7 @@ def __init__(self, url, etag, urlopen=web_util.urlopen):
|
||||
|
||||
def conditional_fetch(self) -> FetchIndexResult:
|
||||
# Just do a conditional fetch immediately
|
||||
url = url_util.join(self.url, _build_cache_relative_path, "index.json")
|
||||
url = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, "index.json")
|
||||
headers = {
|
||||
"User-Agent": web_util.SPACK_USER_AGENT,
|
||||
"If-None-Match": '"{}"'.format(self.etag),
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"):
|
||||
@ -778,7 +789,7 @@ def setup_package(pkg, dirty, context: Context = Context.BUILD):
|
||||
for mod in ["cray-mpich", "cray-libsci"]:
|
||||
module("unload", mod)
|
||||
|
||||
if target.module_name:
|
||||
if target and target.module_name:
|
||||
load_module(target.module_name)
|
||||
|
||||
load_external_modules(pkg)
|
||||
@ -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
|
||||
|
@ -9,11 +9,10 @@
|
||||
import shutil
|
||||
from os.path import basename, dirname, isdir
|
||||
|
||||
from llnl.util.filesystem import find_headers, find_libraries, join_path
|
||||
from llnl.util.filesystem import find_headers, find_libraries, join_path, mkdirp
|
||||
from llnl.util.link_tree import LinkTree
|
||||
|
||||
from spack.directives import conflicts, variant
|
||||
from spack.package import mkdirp
|
||||
from spack.util.environment import EnvironmentModifications
|
||||
from spack.util.executable import Executable
|
||||
|
||||
@ -212,3 +211,7 @@ def link_flags(self):
|
||||
@property
|
||||
def ld_flags(self):
|
||||
return "{0} {1}".format(self.search_flags, self.link_flags)
|
||||
|
||||
|
||||
#: Tuple of Intel math libraries, exported to packages
|
||||
INTEL_MATH_LIBRARIES = ("intel-mkl", "intel-oneapi-mkl", "intel-parallel-studio")
|
||||
|
@ -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):
|
||||
|
@ -7,13 +7,14 @@
|
||||
import glob
|
||||
import hashlib
|
||||
import json
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.request
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
import llnl.util.tty as tty
|
||||
from llnl.string import plural
|
||||
@ -307,8 +308,30 @@ def _progress(i: int, total: int):
|
||||
return ""
|
||||
|
||||
|
||||
def _make_pool():
|
||||
return multiprocessing.pool.Pool(determine_number_of_jobs(parallel=True))
|
||||
class NoPool:
|
||||
def map(self, func, args):
|
||||
return [func(a) for a in args]
|
||||
|
||||
def starmap(self, func, args):
|
||||
return [func(*a) for a in args]
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
pass
|
||||
|
||||
|
||||
MaybePool = Union[multiprocessing.pool.Pool, NoPool]
|
||||
|
||||
|
||||
def _make_pool() -> MaybePool:
|
||||
"""Can't use threading because it's unsafe, and can't use spawned processes because of globals.
|
||||
That leaves only forking"""
|
||||
if multiprocessing.get_start_method() == "fork":
|
||||
return multiprocessing.pool.Pool(determine_number_of_jobs(parallel=True))
|
||||
else:
|
||||
return NoPool()
|
||||
|
||||
|
||||
def push_fn(args):
|
||||
@ -591,7 +614,7 @@ def _push_oci(
|
||||
image_ref: ImageReference,
|
||||
installed_specs_with_deps: List[Spec],
|
||||
tmpdir: str,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
pool: MaybePool,
|
||||
) -> List[str]:
|
||||
"""Push specs to an OCI registry
|
||||
|
||||
@ -692,11 +715,10 @@ def _config_from_tag(image_ref: ImageReference, tag: str) -> Optional[dict]:
|
||||
return config if "spec" in config else None
|
||||
|
||||
|
||||
def _update_index_oci(
|
||||
image_ref: ImageReference, tmpdir: str, pool: multiprocessing.pool.Pool
|
||||
) -> None:
|
||||
response = spack.oci.opener.urlopen(urllib.request.Request(url=image_ref.tags_url()))
|
||||
spack.oci.opener.ensure_status(response, 200)
|
||||
def _update_index_oci(image_ref: ImageReference, tmpdir: str, pool: MaybePool) -> None:
|
||||
request = urllib.request.Request(url=image_ref.tags_url())
|
||||
response = spack.oci.opener.urlopen(request)
|
||||
spack.oci.opener.ensure_status(request, response, 200)
|
||||
tags = json.load(response)["tags"]
|
||||
|
||||
# Fetch all image config files in parallel
|
||||
|
30
lib/spack/spack/cmd/common/confirmation.py
Normal file
30
lib/spack/spack/cmd/common/confirmation.py
Normal file
@ -0,0 +1,30 @@
|
||||
# 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)
|
||||
|
||||
import sys
|
||||
from typing import List
|
||||
|
||||
import llnl.util.tty as tty
|
||||
|
||||
import spack.cmd
|
||||
|
||||
display_args = {"long": True, "show_flags": False, "variants": False, "indent": 4}
|
||||
|
||||
|
||||
def confirm_action(specs: List[spack.spec.Spec], participle: str, noun: str):
|
||||
"""Display the list of specs to be acted on and ask for confirmation.
|
||||
|
||||
Args:
|
||||
specs: specs to be removed
|
||||
participle: action expressed as a participle, e.g. "uninstalled"
|
||||
noun: action expressed as a noun, e.g. "uninstallation"
|
||||
"""
|
||||
tty.msg(f"The following {len(specs)} packages will be {participle}:\n")
|
||||
spack.cmd.display_specs(specs, **display_args)
|
||||
print("")
|
||||
answer = tty.get_yes_or_no("Do you want to proceed?", default=False)
|
||||
if not answer:
|
||||
tty.msg(f"Aborting {noun}")
|
||||
sys.exit(0)
|
103
lib/spack/spack/cmd/deconcretize.py
Normal file
103
lib/spack/spack/cmd/deconcretize.py
Normal file
@ -0,0 +1,103 @@
|
||||
# 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)
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from typing import List
|
||||
|
||||
import llnl.util.tty as tty
|
||||
|
||||
import spack.cmd
|
||||
import spack.cmd.common.arguments as arguments
|
||||
import spack.cmd.common.confirmation as confirmation
|
||||
import spack.environment as ev
|
||||
import spack.spec
|
||||
|
||||
description = "remove specs from the concretized lockfile of an environment"
|
||||
section = "environments"
|
||||
level = "long"
|
||||
|
||||
# Arguments for display_specs when we find ambiguity
|
||||
display_args = {"long": True, "show_flags": False, "variants": False, "indent": 4}
|
||||
|
||||
|
||||
def setup_parser(subparser):
|
||||
subparser.add_argument(
|
||||
"--root", action="store_true", help="deconcretize only specific environment roots"
|
||||
)
|
||||
arguments.add_common_arguments(subparser, ["yes_to_all", "specs"])
|
||||
subparser.add_argument(
|
||||
"-a",
|
||||
"--all",
|
||||
action="store_true",
|
||||
dest="all",
|
||||
help="deconcretize ALL specs that match each supplied spec",
|
||||
)
|
||||
|
||||
|
||||
def get_deconcretize_list(
|
||||
args: argparse.Namespace, specs: List[spack.spec.Spec], env: ev.Environment
|
||||
) -> List[spack.spec.Spec]:
|
||||
"""
|
||||
Get list of environment roots to deconcretize
|
||||
"""
|
||||
env_specs = [s for _, s in env.concretized_specs()]
|
||||
to_deconcretize = []
|
||||
errors = []
|
||||
|
||||
for s in specs:
|
||||
if args.root:
|
||||
# find all roots matching given spec
|
||||
to_deconc = [e for e in env_specs if e.satisfies(s)]
|
||||
else:
|
||||
# find all roots matching or depending on a matching spec
|
||||
to_deconc = [e for e in env_specs if any(d.satisfies(s) for d in e.traverse())]
|
||||
|
||||
if len(to_deconc) < 1:
|
||||
tty.warn(f"No matching specs to deconcretize for {s}")
|
||||
|
||||
elif len(to_deconc) > 1 and not args.all:
|
||||
errors.append((s, to_deconc))
|
||||
|
||||
to_deconcretize.extend(to_deconc)
|
||||
|
||||
if errors:
|
||||
for spec, matching in errors:
|
||||
tty.error(f"{spec} matches multiple concrete specs:")
|
||||
sys.stderr.write("\n")
|
||||
spack.cmd.display_specs(matching, output=sys.stderr, **display_args)
|
||||
sys.stderr.write("\n")
|
||||
sys.stderr.flush()
|
||||
tty.die("Use '--all' to deconcretize all matching specs, or be more specific")
|
||||
|
||||
return to_deconcretize
|
||||
|
||||
|
||||
def deconcretize_specs(args, specs):
|
||||
env = spack.cmd.require_active_env(cmd_name="deconcretize")
|
||||
|
||||
if args.specs:
|
||||
deconcretize_list = get_deconcretize_list(args, specs, env)
|
||||
else:
|
||||
deconcretize_list = [s for _, s in env.concretized_specs()]
|
||||
|
||||
if not args.yes_to_all:
|
||||
confirmation.confirm_action(deconcretize_list, "deconcretized", "deconcretization")
|
||||
|
||||
with env.write_transaction():
|
||||
for spec in deconcretize_list:
|
||||
env.deconcretize(spec)
|
||||
env.write()
|
||||
|
||||
|
||||
def deconcretize(parser, args):
|
||||
if not args.specs and not args.all:
|
||||
tty.die(
|
||||
"deconcretize requires at least one spec argument.",
|
||||
" Use `spack deconcretize --all` to deconcretize ALL specs.",
|
||||
)
|
||||
|
||||
specs = spack.cmd.parse_specs(args.specs) if args.specs else [any]
|
||||
deconcretize_specs(args, specs)
|
@ -6,6 +6,7 @@
|
||||
import llnl.util.tty as tty
|
||||
|
||||
import spack.cmd.common.arguments
|
||||
import spack.cmd.common.confirmation
|
||||
import spack.cmd.uninstall
|
||||
import spack.environment as ev
|
||||
import spack.store
|
||||
@ -41,6 +42,6 @@ def gc(parser, args):
|
||||
return
|
||||
|
||||
if not args.yes_to_all:
|
||||
spack.cmd.uninstall.confirm_removal(specs)
|
||||
spack.cmd.common.confirmation.confirm_action(specs, "uninstalled", "uninstallation")
|
||||
|
||||
spack.cmd.uninstall.do_uninstall(specs, force=False)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
import sys
|
||||
import textwrap
|
||||
from itertools import zip_longest
|
||||
|
||||
@ -16,6 +17,7 @@
|
||||
import spack.install_test
|
||||
import spack.repo
|
||||
import spack.spec
|
||||
import spack.version
|
||||
from spack.package_base import preferred_version
|
||||
|
||||
description = "get detailed information on a particular package"
|
||||
@ -53,6 +55,7 @@ def setup_parser(subparser):
|
||||
("--tags", print_tags.__doc__),
|
||||
("--tests", print_tests.__doc__),
|
||||
("--virtuals", print_virtuals.__doc__),
|
||||
("--variants-by-name", "list variants in strict name order; don't group by condition"),
|
||||
]
|
||||
for opt, help_comment in options:
|
||||
subparser.add_argument(opt, action="store_true", help=help_comment)
|
||||
@ -77,35 +80,10 @@ def license(s):
|
||||
|
||||
|
||||
class VariantFormatter:
|
||||
def __init__(self, variants):
|
||||
self.variants = variants
|
||||
def __init__(self, pkg):
|
||||
self.variants = pkg.variants
|
||||
self.headers = ("Name [Default]", "When", "Allowed values", "Description")
|
||||
|
||||
# Formats
|
||||
fmt_name = "{0} [{1}]"
|
||||
|
||||
# Initialize column widths with the length of the
|
||||
# corresponding headers, as they cannot be shorter
|
||||
# than that
|
||||
self.column_widths = [len(x) for x in self.headers]
|
||||
|
||||
# Expand columns based on max line lengths
|
||||
for k, e in variants.items():
|
||||
v, w = e
|
||||
candidate_max_widths = (
|
||||
len(fmt_name.format(k, self.default(v))), # Name [Default]
|
||||
len(str(w)),
|
||||
len(v.allowed_values), # Allowed values
|
||||
len(v.description), # Description
|
||||
)
|
||||
|
||||
self.column_widths = (
|
||||
max(self.column_widths[0], candidate_max_widths[0]),
|
||||
max(self.column_widths[1], candidate_max_widths[1]),
|
||||
max(self.column_widths[2], candidate_max_widths[2]),
|
||||
max(self.column_widths[3], candidate_max_widths[3]),
|
||||
)
|
||||
|
||||
# Don't let name or possible values be less than max widths
|
||||
_, cols = tty.terminal_size()
|
||||
max_name = min(self.column_widths[0], 30)
|
||||
@ -137,6 +115,8 @@ def default(self, v):
|
||||
def lines(self):
|
||||
if not self.variants:
|
||||
yield " None"
|
||||
return
|
||||
|
||||
else:
|
||||
yield " " + self.fmt % self.headers
|
||||
underline = tuple([w * "=" for w in self.column_widths])
|
||||
@ -271,15 +251,165 @@ def print_tests(pkg):
|
||||
color.cprint(" None")
|
||||
|
||||
|
||||
def print_variants(pkg):
|
||||
def _fmt_value(v):
|
||||
if v is None or isinstance(v, bool):
|
||||
return str(v).lower()
|
||||
else:
|
||||
return str(v)
|
||||
|
||||
|
||||
def _fmt_name_and_default(variant):
|
||||
"""Print colorized name [default] for a variant."""
|
||||
return color.colorize(f"@c{{{variant.name}}} @C{{[{_fmt_value(variant.default)}]}}")
|
||||
|
||||
|
||||
def _fmt_when(when, indent):
|
||||
return color.colorize(f"{indent * ' '}@B{{when}} {color.cescape(when)}")
|
||||
|
||||
|
||||
def _fmt_variant_description(variant, width, indent):
|
||||
"""Format a variant's description, preserving explicit line breaks."""
|
||||
return "\n".join(
|
||||
textwrap.fill(
|
||||
line, width=width, initial_indent=indent * " ", subsequent_indent=indent * " "
|
||||
)
|
||||
for line in variant.description.split("\n")
|
||||
)
|
||||
|
||||
|
||||
def _fmt_variant(variant, max_name_default_len, indent, when=None, out=None):
|
||||
out = out or sys.stdout
|
||||
|
||||
_, cols = tty.terminal_size()
|
||||
|
||||
name_and_default = _fmt_name_and_default(variant)
|
||||
name_default_len = color.clen(name_and_default)
|
||||
|
||||
values = variant.values
|
||||
if not isinstance(variant.values, (tuple, list, spack.variant.DisjointSetsOfValues)):
|
||||
values = [variant.values]
|
||||
|
||||
# put 'none' first, sort the rest by value
|
||||
sorted_values = sorted(values, key=lambda v: (v != "none", v))
|
||||
|
||||
pad = 4 # min padding between 'name [default]' and values
|
||||
value_indent = (indent + max_name_default_len + pad) * " " # left edge of values
|
||||
|
||||
# This preserves any formatting (i.e., newlines) from how the description was
|
||||
# written in package.py, but still wraps long lines for small terminals.
|
||||
# This allows some packages to provide detailed help on their variants (see, e.g., gasnet).
|
||||
formatted_values = "\n".join(
|
||||
textwrap.wrap(
|
||||
f"{', '.join(_fmt_value(v) for v in sorted_values)}",
|
||||
width=cols - 2,
|
||||
initial_indent=value_indent,
|
||||
subsequent_indent=value_indent,
|
||||
)
|
||||
)
|
||||
formatted_values = formatted_values[indent + name_default_len + pad :]
|
||||
|
||||
# name [default] value1, value2, value3, ...
|
||||
padding = pad * " "
|
||||
color.cprint(f"{indent * ' '}{name_and_default}{padding}@c{{{formatted_values}}}", stream=out)
|
||||
|
||||
# when <spec>
|
||||
description_indent = indent + 4
|
||||
if when is not None and when != spack.spec.Spec():
|
||||
out.write(_fmt_when(when, description_indent - 2))
|
||||
out.write("\n")
|
||||
|
||||
# description, preserving explicit line breaks from the way it's written in the package file
|
||||
out.write(_fmt_variant_description(variant, cols - 2, description_indent))
|
||||
out.write("\n")
|
||||
|
||||
|
||||
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 sorted(pkg.variants.items()):
|
||||
for when in whens:
|
||||
variants.setdefault(name, {}).setdefault(when, []).append(variant)
|
||||
return variants
|
||||
|
||||
|
||||
def _variants_by_when_name(pkg):
|
||||
"""Adaptor to get variants keyed by { when: { name: Variant } }"""
|
||||
# TODO: replace with pkg.variants when unified directive dicts are merged.
|
||||
variants = {}
|
||||
for name, (variant, whens) in pkg.variants.items():
|
||||
for when in whens:
|
||||
variants.setdefault(when, {})[name] = variant
|
||||
return variants
|
||||
|
||||
|
||||
def _print_variants_header(pkg):
|
||||
"""output variants"""
|
||||
|
||||
if not pkg.variants:
|
||||
print(" None")
|
||||
return
|
||||
|
||||
color.cprint("")
|
||||
color.cprint(section_title("Variants:"))
|
||||
|
||||
formatter = VariantFormatter(pkg.variants)
|
||||
for line in formatter.lines:
|
||||
color.cprint(color.cescape(line))
|
||||
variants_by_name = _variants_by_name_when(pkg)
|
||||
|
||||
# Calculate the max length of the "name [default]" part of the variant display
|
||||
# This lets us know where to print variant values.
|
||||
max_name_default_len = max(
|
||||
color.clen(_fmt_name_and_default(variant))
|
||||
for name, when_variants in variants_by_name.items()
|
||||
for variants in when_variants.values()
|
||||
for variant in variants
|
||||
)
|
||||
|
||||
return max_name_default_len, variants_by_name
|
||||
|
||||
|
||||
def _unconstrained_ver_first(item):
|
||||
"""sort key that puts specs with open version ranges first"""
|
||||
spec, _ = item
|
||||
return (spack.version.any_version not in spec.versions, spec)
|
||||
|
||||
|
||||
def print_variants_grouped_by_when(pkg):
|
||||
max_name_default_len, _ = _print_variants_header(pkg)
|
||||
|
||||
indent = 4
|
||||
variants = _variants_by_when_name(pkg)
|
||||
for when, variants_by_name in sorted(variants.items(), key=_unconstrained_ver_first):
|
||||
padded_values = max_name_default_len + 4
|
||||
start_indent = indent
|
||||
|
||||
if when != spack.spec.Spec():
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.write(_fmt_when(when, indent))
|
||||
sys.stdout.write("\n")
|
||||
|
||||
# indent names slightly inside 'when', but line up values
|
||||
padded_values -= 2
|
||||
start_indent += 2
|
||||
|
||||
for name, variant in sorted(variants_by_name.items()):
|
||||
_fmt_variant(variant, padded_values, start_indent, None, out=sys.stdout)
|
||||
|
||||
|
||||
def print_variants_by_name(pkg):
|
||||
max_name_default_len, variants_by_name = _print_variants_header(pkg)
|
||||
max_name_default_len += 4
|
||||
|
||||
indent = 4
|
||||
for name, when_variants in variants_by_name.items():
|
||||
for when, variants in sorted(when_variants.items(), key=_unconstrained_ver_first):
|
||||
for variant in variants:
|
||||
_fmt_variant(variant, max_name_default_len, indent, when, out=sys.stdout)
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
def print_variants(pkg):
|
||||
"""output variants"""
|
||||
print_variants_grouped_by_when(pkg)
|
||||
|
||||
|
||||
def print_versions(pkg):
|
||||
@ -300,18 +430,24 @@ def print_versions(pkg):
|
||||
pad = padder(pkg.versions, 4)
|
||||
|
||||
preferred = preferred_version(pkg)
|
||||
url = ""
|
||||
if pkg.has_code:
|
||||
url = fs.for_package_version(pkg, preferred)
|
||||
|
||||
def get_url(version):
|
||||
try:
|
||||
return fs.for_package_version(pkg, version)
|
||||
except spack.fetch_strategy.InvalidArgsError:
|
||||
return "No URL"
|
||||
|
||||
url = get_url(preferred) if pkg.has_code else ""
|
||||
line = version(" {0}".format(pad(preferred))) + color.cescape(url)
|
||||
color.cprint(line)
|
||||
color.cwrite(line)
|
||||
|
||||
print()
|
||||
|
||||
safe = []
|
||||
deprecated = []
|
||||
for v in reversed(sorted(pkg.versions)):
|
||||
if pkg.has_code:
|
||||
url = fs.for_package_version(pkg, v)
|
||||
url = get_url(v)
|
||||
if pkg.versions[v].get("deprecated", False):
|
||||
deprecated.append((v, url))
|
||||
else:
|
||||
@ -384,7 +520,12 @@ def info(parser, args):
|
||||
else:
|
||||
color.cprint(" None")
|
||||
|
||||
color.cprint(section_title("Homepage: ") + pkg.homepage)
|
||||
if getattr(pkg, "homepage"):
|
||||
color.cprint(section_title("Homepage: ") + pkg.homepage)
|
||||
|
||||
_print_variants = (
|
||||
print_variants_by_name if args.variants_by_name else print_variants_grouped_by_when
|
||||
)
|
||||
|
||||
# Now output optional information in expected order
|
||||
sections = [
|
||||
@ -392,7 +533,7 @@ def info(parser, args):
|
||||
(args.all or args.detectable, print_detectable),
|
||||
(args.all or args.tags, print_tags),
|
||||
(args.all or not args.no_versions, print_versions),
|
||||
(args.all or not args.no_variants, print_variants),
|
||||
(args.all or not args.no_variants, _print_variants),
|
||||
(args.all or args.phases, print_phases),
|
||||
(args.all or not args.no_dependencies, print_dependencies),
|
||||
(args.all or args.virtuals, print_virtuals),
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
|
||||
# tutorial configuration parameters
|
||||
tutorial_branch = "releases/v0.20"
|
||||
tutorial_branch = "releases/v0.21"
|
||||
tutorial_mirror = "file:///mirror"
|
||||
tutorial_key = os.path.join(spack.paths.share_path, "keys", "tutorial.pub")
|
||||
|
||||
|
@ -11,10 +11,9 @@
|
||||
|
||||
import spack.cmd
|
||||
import spack.cmd.common.arguments as arguments
|
||||
import spack.cmd.common.confirmation as confirmation
|
||||
import spack.environment as ev
|
||||
import spack.error
|
||||
import spack.package_base
|
||||
import spack.repo
|
||||
import spack.spec
|
||||
import spack.store
|
||||
import spack.traverse as traverse
|
||||
@ -278,7 +277,7 @@ def uninstall_specs(args, specs):
|
||||
return
|
||||
|
||||
if not args.yes_to_all:
|
||||
confirm_removal(uninstall_list)
|
||||
confirmation.confirm_action(uninstall_list, "uninstalled", "uninstallation")
|
||||
|
||||
# Uninstall everything on the list
|
||||
do_uninstall(uninstall_list, args.force)
|
||||
@ -292,21 +291,6 @@ def uninstall_specs(args, specs):
|
||||
env.regenerate_views()
|
||||
|
||||
|
||||
def confirm_removal(specs: List[spack.spec.Spec]):
|
||||
"""Display the list of specs to be removed and ask for confirmation.
|
||||
|
||||
Args:
|
||||
specs: specs to be removed
|
||||
"""
|
||||
tty.msg("The following {} packages will be uninstalled:\n".format(len(specs)))
|
||||
spack.cmd.display_specs(specs, **display_args)
|
||||
print("")
|
||||
answer = tty.get_yes_or_no("Do you want to proceed?", default=False)
|
||||
if not answer:
|
||||
tty.msg("Aborting uninstallation")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def uninstall(parser, args):
|
||||
if not args.specs and not args.all:
|
||||
tty.die(
|
||||
|
@ -112,16 +112,16 @@ def _to_dict(compiler):
|
||||
def get_compiler_config(scope=None, init_config=True):
|
||||
"""Return the compiler configuration for the specified architecture."""
|
||||
|
||||
config = spack.config.get("compilers", scope=scope) or []
|
||||
config = spack.config.CONFIG.get("compilers", scope=scope) or []
|
||||
if config or not init_config:
|
||||
return config
|
||||
|
||||
merged_config = spack.config.get("compilers")
|
||||
merged_config = spack.config.CONFIG.get("compilers")
|
||||
if merged_config:
|
||||
return config
|
||||
|
||||
_init_compiler_config(scope=scope)
|
||||
config = spack.config.get("compilers", scope=scope)
|
||||
config = spack.config.CONFIG.get("compilers", scope=scope)
|
||||
return config
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -506,9 +514,10 @@ def get_compilers(config, cspec=None, arch_spec=None):
|
||||
for items in config:
|
||||
items = items["compiler"]
|
||||
|
||||
# NOTE: in principle this should be equality not satisfies, but config can still
|
||||
# be written in old format gcc@10.1.0 instead of gcc@=10.1.0.
|
||||
if cspec and not cspec.satisfies(items["spec"]):
|
||||
# We might use equality here.
|
||||
if cspec and not spack.spec.parse_with_version_concrete(
|
||||
items["spec"], compiler=True
|
||||
).satisfies(cspec):
|
||||
continue
|
||||
|
||||
# If an arch spec is given, confirm that this compiler
|
||||
|
@ -9,6 +9,8 @@
|
||||
import sys
|
||||
from typing import Dict, List, Set
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import spack.compiler
|
||||
import spack.operating_systems.windows_os
|
||||
import spack.platforms
|
||||
@ -185,6 +187,9 @@ def __init__(self, *args, **kwargs):
|
||||
# get current platform architecture and format for vcvars argument
|
||||
arch = spack.platforms.real_host().default.lower()
|
||||
arch = arch.replace("-", "_")
|
||||
if str(archspec.cpu.host().family) == "x86_64":
|
||||
arch = "amd64"
|
||||
|
||||
self.vcvars_call = VCVarsInvocation(vcvars_script_path, arch, self.msvc_version)
|
||||
env_cmds.append(self.vcvars_call)
|
||||
# Below is a check for a valid fortran path
|
||||
|
@ -463,6 +463,8 @@ def _depends_on(pkg, spec, when=None, type=dt.DEFAULT_TYPES, patches=None):
|
||||
dep_spec = spack.spec.Spec(spec)
|
||||
if not dep_spec.name:
|
||||
raise DependencyError("Invalid dependency specification in package '%s':" % pkg.name, spec)
|
||||
elif dep_spec.name in ("c", "cxx", "fortran"): # forward compat for language deps
|
||||
return
|
||||
if pkg.name == dep_spec.name:
|
||||
raise CircularReferenceError("Package '%s' cannot depend on itself." % pkg.name)
|
||||
|
||||
|
@ -1358,7 +1358,7 @@ def concretize(self, force=False, tests=False):
|
||||
|
||||
# Remove concrete specs that no longer correlate to a user spec
|
||||
for spec in set(self.concretized_user_specs) - set(self.user_specs):
|
||||
self.deconcretize(spec)
|
||||
self.deconcretize(spec, concrete=False)
|
||||
|
||||
# Pick the right concretization strategy
|
||||
if self.unify == "when_possible":
|
||||
@ -1373,15 +1373,36 @@ def concretize(self, force=False, tests=False):
|
||||
msg = "concretization strategy not implemented [{0}]"
|
||||
raise SpackEnvironmentError(msg.format(self.unify))
|
||||
|
||||
def deconcretize(self, spec):
|
||||
def deconcretize(self, spec: spack.spec.Spec, concrete: bool = True):
|
||||
"""
|
||||
Remove specified spec from environment concretization
|
||||
|
||||
Arguments:
|
||||
spec: Spec to deconcretize. This must be a root of the environment
|
||||
concrete: If True, find all instances of spec as concrete in the environemnt.
|
||||
If False, find a single instance of the abstract spec as root of the environment.
|
||||
"""
|
||||
# spec has to be a root of the environment
|
||||
index = self.concretized_user_specs.index(spec)
|
||||
dag_hash = self.concretized_order.pop(index)
|
||||
del self.concretized_user_specs[index]
|
||||
if concrete:
|
||||
dag_hash = spec.dag_hash()
|
||||
|
||||
pairs = zip(self.concretized_user_specs, self.concretized_order)
|
||||
filtered = [(spec, h) for spec, h in pairs if h != dag_hash]
|
||||
# Cannot use zip and unpack two values; it fails if filtered is empty
|
||||
self.concretized_user_specs = [s for s, _ in filtered]
|
||||
self.concretized_order = [h for _, h in filtered]
|
||||
else:
|
||||
index = self.concretized_user_specs.index(spec)
|
||||
dag_hash = self.concretized_order.pop(index)
|
||||
|
||||
del self.concretized_user_specs[index]
|
||||
|
||||
# If this was the only user spec that concretized to this concrete spec, remove it
|
||||
if dag_hash not in self.concretized_order:
|
||||
del self.specs_by_hash[dag_hash]
|
||||
# if we deconcretized a dependency that doesn't correspond to a root, it
|
||||
# won't be here.
|
||||
if dag_hash in self.specs_by_hash:
|
||||
del self.specs_by_hash[dag_hash]
|
||||
|
||||
def _get_specs_to_concretize(
|
||||
self,
|
||||
@ -1739,11 +1760,14 @@ def _env_modifications_for_view(
|
||||
self, view: ViewDescriptor, reverse: bool = False
|
||||
) -> spack.util.environment.EnvironmentModifications:
|
||||
try:
|
||||
mods = uenv.environment_modifications_for_specs(*self.concrete_roots(), view=view)
|
||||
with spack.store.STORE.db.read_transaction():
|
||||
installed_roots = [s for s in self.concrete_roots() if s.installed]
|
||||
mods = uenv.environment_modifications_for_specs(*installed_roots, view=view)
|
||||
except Exception as e:
|
||||
# Failing to setup spec-specific changes shouldn't be a hard error.
|
||||
tty.warn(
|
||||
"couldn't load runtime environment due to {}: {}".format(e.__class__.__name__, e)
|
||||
f"could not {'unload' if reverse else 'load'} runtime environment due "
|
||||
f"to {e.__class__.__name__}: {e}"
|
||||
)
|
||||
return spack.util.environment.EnvironmentModifications()
|
||||
return mods.reversed() if reverse else mods
|
||||
|
@ -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:
|
||||
|
@ -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(cmd)
|
||||
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.
|
||||
|
@ -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]
|
||||
|
@ -486,43 +486,35 @@ def excluded(self):
|
||||
spec = self.spec
|
||||
conf = self.module.configuration(self.name)
|
||||
|
||||
# Compute the list of include rules that match
|
||||
include_rules = conf.get("include", [])
|
||||
include_matches = [x for x in include_rules if spec.satisfies(x)]
|
||||
|
||||
# Compute the list of exclude rules that match
|
||||
exclude_rules = conf.get("exclude", [])
|
||||
exclude_matches = [x for x in exclude_rules if spec.satisfies(x)]
|
||||
# Compute the list of matching include / exclude rules, and whether excluded as implicit
|
||||
include_matches = [x for x in conf.get("include", []) if spec.satisfies(x)]
|
||||
exclude_matches = [x for x in conf.get("exclude", []) if spec.satisfies(x)]
|
||||
excluded_as_implicit = not self.explicit and conf.get("exclude_implicits", False)
|
||||
|
||||
def debug_info(line_header, match_list):
|
||||
if match_list:
|
||||
msg = "\t{0} : {1}".format(line_header, spec.cshort_spec)
|
||||
tty.debug(msg)
|
||||
tty.debug(f"\t{line_header} : {spec.cshort_spec}")
|
||||
for rule in match_list:
|
||||
tty.debug("\t\tmatches rule: {0}".format(rule))
|
||||
tty.debug(f"\t\tmatches rule: {rule}")
|
||||
|
||||
debug_info("INCLUDE", include_matches)
|
||||
debug_info("EXCLUDE", exclude_matches)
|
||||
|
||||
if not include_matches and exclude_matches:
|
||||
return True
|
||||
if excluded_as_implicit:
|
||||
tty.debug(f"\tEXCLUDED_AS_IMPLICIT : {spec.cshort_spec}")
|
||||
|
||||
return False
|
||||
return not include_matches and (exclude_matches or excluded_as_implicit)
|
||||
|
||||
@property
|
||||
def hidden(self):
|
||||
"""Returns True if the module has been hidden, False otherwise."""
|
||||
|
||||
# A few variables for convenience of writing the method
|
||||
spec = self.spec
|
||||
conf = self.module.configuration(self.name)
|
||||
|
||||
hidden_as_implicit = not self.explicit and conf.get(
|
||||
"hide_implicits", conf.get("exclude_implicits", False)
|
||||
)
|
||||
hidden_as_implicit = not self.explicit and conf.get("hide_implicits", False)
|
||||
|
||||
if hidden_as_implicit:
|
||||
tty.debug(f"\tHIDDEN_AS_IMPLICIT : {spec.cshort_spec}")
|
||||
tty.debug(f"\tHIDDEN_AS_IMPLICIT : {self.spec.cshort_spec}")
|
||||
|
||||
return hidden_as_implicit
|
||||
|
||||
|
@ -134,7 +134,7 @@ def upload_blob(
|
||||
return True
|
||||
|
||||
# Otherwise, do another PUT request.
|
||||
spack.oci.opener.ensure_status(response, 202)
|
||||
spack.oci.opener.ensure_status(request, response, 202)
|
||||
assert "Location" in response.headers
|
||||
|
||||
# Can be absolute or relative, joining handles both
|
||||
@ -143,19 +143,16 @@ def upload_blob(
|
||||
)
|
||||
f.seek(0)
|
||||
|
||||
response = _urlopen(
|
||||
Request(
|
||||
url=upload_url,
|
||||
method="PUT",
|
||||
data=f,
|
||||
headers={
|
||||
"Content-Type": "application/octet-stream",
|
||||
"Content-Length": str(file_size),
|
||||
},
|
||||
)
|
||||
request = Request(
|
||||
url=upload_url,
|
||||
method="PUT",
|
||||
data=f,
|
||||
headers={"Content-Type": "application/octet-stream", "Content-Length": str(file_size)},
|
||||
)
|
||||
|
||||
spack.oci.opener.ensure_status(response, 201)
|
||||
response = _urlopen(request)
|
||||
|
||||
spack.oci.opener.ensure_status(request, response, 201)
|
||||
|
||||
# print elapsed time and # MB/s
|
||||
_log_upload_progress(digest, file_size, time.time() - start)
|
||||
@ -189,16 +186,16 @@ def upload_manifest(
|
||||
if not tag:
|
||||
ref = ref.with_digest(digest)
|
||||
|
||||
response = _urlopen(
|
||||
Request(
|
||||
url=ref.manifest_url(),
|
||||
method="PUT",
|
||||
data=data,
|
||||
headers={"Content-Type": oci_manifest["mediaType"]},
|
||||
)
|
||||
request = Request(
|
||||
url=ref.manifest_url(),
|
||||
method="PUT",
|
||||
data=data,
|
||||
headers={"Content-Type": oci_manifest["mediaType"]},
|
||||
)
|
||||
|
||||
spack.oci.opener.ensure_status(response, 201)
|
||||
response = _urlopen(request)
|
||||
|
||||
spack.oci.opener.ensure_status(request, response, 201)
|
||||
return digest, size
|
||||
|
||||
|
||||
|
@ -310,19 +310,15 @@ def http_error_401(self, req: Request, fp, code, msg, headers):
|
||||
# Login failed, avoid infinite recursion where we go back and
|
||||
# forth between auth server and registry
|
||||
if hasattr(req, "login_attempted"):
|
||||
raise urllib.error.HTTPError(
|
||||
req.full_url, code, f"Failed to login to {req.full_url}: {msg}", headers, fp
|
||||
raise spack.util.web.DetailedHTTPError(
|
||||
req, code, f"Failed to login: {msg}", headers, fp
|
||||
)
|
||||
|
||||
# On 401 Unauthorized, parse the WWW-Authenticate header
|
||||
# to determine what authentication is required
|
||||
if "WWW-Authenticate" not in headers:
|
||||
raise urllib.error.HTTPError(
|
||||
req.full_url,
|
||||
code,
|
||||
"Cannot login to registry, missing WWW-Authenticate header",
|
||||
headers,
|
||||
fp,
|
||||
raise spack.util.web.DetailedHTTPError(
|
||||
req, code, "Cannot login to registry, missing WWW-Authenticate header", headers, fp
|
||||
)
|
||||
|
||||
header_value = headers["WWW-Authenticate"]
|
||||
@ -330,8 +326,8 @@ def http_error_401(self, req: Request, fp, code, msg, headers):
|
||||
try:
|
||||
challenge = get_bearer_challenge(parse_www_authenticate(header_value))
|
||||
except ValueError as e:
|
||||
raise urllib.error.HTTPError(
|
||||
req.full_url,
|
||||
raise spack.util.web.DetailedHTTPError(
|
||||
req,
|
||||
code,
|
||||
f"Cannot login to registry, malformed WWW-Authenticate header: {header_value}",
|
||||
headers,
|
||||
@ -340,8 +336,8 @@ def http_error_401(self, req: Request, fp, code, msg, headers):
|
||||
|
||||
# If there is no bearer challenge, we can't handle it
|
||||
if not challenge:
|
||||
raise urllib.error.HTTPError(
|
||||
req.full_url,
|
||||
raise spack.util.web.DetailedHTTPError(
|
||||
req,
|
||||
code,
|
||||
f"Cannot login to registry, unsupported authentication scheme: {header_value}",
|
||||
headers,
|
||||
@ -356,8 +352,8 @@ def http_error_401(self, req: Request, fp, code, msg, headers):
|
||||
timeout=req.timeout,
|
||||
)
|
||||
except ValueError as e:
|
||||
raise urllib.error.HTTPError(
|
||||
req.full_url,
|
||||
raise spack.util.web.DetailedHTTPError(
|
||||
req,
|
||||
code,
|
||||
f"Cannot login to registry, failed to obtain bearer token: {e}",
|
||||
headers,
|
||||
@ -412,13 +408,13 @@ def create_opener():
|
||||
return opener
|
||||
|
||||
|
||||
def ensure_status(response: HTTPResponse, status: int):
|
||||
def ensure_status(request: urllib.request.Request, response: HTTPResponse, status: int):
|
||||
"""Raise an error if the response status is not the expected one."""
|
||||
if response.status == status:
|
||||
return
|
||||
|
||||
raise urllib.error.HTTPError(
|
||||
response.geturl(), response.status, response.reason, response.info(), None
|
||||
raise spack.util.web.DetailedHTTPError(
|
||||
request, response.status, response.reason, response.info(), None
|
||||
)
|
||||
|
||||
|
||||
|
@ -143,6 +143,7 @@ def __init__(self):
|
||||
"12": "monterey",
|
||||
"13": "ventura",
|
||||
"14": "sonoma",
|
||||
"15": "sequoia",
|
||||
}
|
||||
|
||||
version = macos_version()
|
||||
|
@ -49,6 +49,7 @@
|
||||
from spack.build_systems.nmake import NMakePackage
|
||||
from spack.build_systems.octave import OctavePackage
|
||||
from spack.build_systems.oneapi import (
|
||||
INTEL_MATH_LIBRARIES,
|
||||
IntelOneApiLibraryPackage,
|
||||
IntelOneApiPackage,
|
||||
IntelOneApiStaticLibraryList,
|
||||
|
@ -24,8 +24,9 @@
|
||||
import textwrap
|
||||
import time
|
||||
import traceback
|
||||
import typing
|
||||
import warnings
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Type, TypeVar
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union
|
||||
|
||||
import llnl.util.filesystem as fsys
|
||||
import llnl.util.tty as tty
|
||||
@ -682,13 +683,13 @@ def __init__(self, spec):
|
||||
@classmethod
|
||||
def possible_dependencies(
|
||||
cls,
|
||||
transitive=True,
|
||||
expand_virtuals=True,
|
||||
transitive: bool = True,
|
||||
expand_virtuals: bool = True,
|
||||
depflag: dt.DepFlag = dt.ALL,
|
||||
visited=None,
|
||||
missing=None,
|
||||
virtuals=None,
|
||||
):
|
||||
visited: Optional[dict] = None,
|
||||
missing: Optional[dict] = None,
|
||||
virtuals: Optional[set] = None,
|
||||
) -> Dict[str, Set[str]]:
|
||||
"""Return dict of possible dependencies of this package.
|
||||
|
||||
Args:
|
||||
@ -2449,14 +2450,21 @@ def flatten_dependencies(spec, flat_dir):
|
||||
dep_files.merge(flat_dir + "/" + name)
|
||||
|
||||
|
||||
def possible_dependencies(*pkg_or_spec, **kwargs):
|
||||
def possible_dependencies(
|
||||
*pkg_or_spec: Union[str, spack.spec.Spec, typing.Type[PackageBase]],
|
||||
transitive: bool = True,
|
||||
expand_virtuals: bool = True,
|
||||
depflag: dt.DepFlag = dt.ALL,
|
||||
missing: Optional[dict] = None,
|
||||
virtuals: Optional[set] = None,
|
||||
) -> Dict[str, Set[str]]:
|
||||
"""Get the possible dependencies of a number of packages.
|
||||
|
||||
See ``PackageBase.possible_dependencies`` for details.
|
||||
"""
|
||||
packages = []
|
||||
for pos in pkg_or_spec:
|
||||
if isinstance(pos, PackageMeta):
|
||||
if isinstance(pos, PackageMeta) and issubclass(pos, PackageBase):
|
||||
packages.append(pos)
|
||||
continue
|
||||
|
||||
@ -2469,9 +2477,16 @@ def possible_dependencies(*pkg_or_spec, **kwargs):
|
||||
else:
|
||||
packages.append(pos.package_class)
|
||||
|
||||
visited = {}
|
||||
visited: Dict[str, Set[str]] = {}
|
||||
for pkg in packages:
|
||||
pkg.possible_dependencies(visited=visited, **kwargs)
|
||||
pkg.possible_dependencies(
|
||||
visited=visited,
|
||||
transitive=transitive,
|
||||
expand_virtuals=expand_virtuals,
|
||||
depflag=depflag,
|
||||
missing=missing,
|
||||
virtuals=virtuals,
|
||||
)
|
||||
|
||||
return visited
|
||||
|
||||
|
@ -490,7 +490,7 @@ def read(self, stream):
|
||||
self.index = spack.tag.TagIndex.from_json(stream, self.repository)
|
||||
|
||||
def update(self, pkg_fullname):
|
||||
self.index.update_package(pkg_fullname)
|
||||
self.index.update_package(pkg_fullname.split(".")[-1])
|
||||
|
||||
def write(self, stream):
|
||||
self.index.to_json(stream)
|
||||
|
@ -18,9 +18,7 @@
|
||||
#: IS ADDED IMMEDIATELY BELOW THE MODULE TYPE ATTRIBUTE
|
||||
spec_regex = (
|
||||
r"(?!hierarchy|core_specs|verbose|hash_length|defaults|filter_hierarchy_specs|hide|"
|
||||
r"whitelist|blacklist|" # DEPRECATED: remove in 0.20.
|
||||
r"include|exclude|" # use these more inclusive/consistent options
|
||||
r"projections|naming_scheme|core_compilers|all)(^\w[\w-]*)"
|
||||
r"include|exclude|projections|naming_scheme|core_compilers|all)(^\w[\w-]*)"
|
||||
)
|
||||
|
||||
#: Matches a valid name for a module set
|
||||
@ -46,14 +44,7 @@
|
||||
"default": {},
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
# DEPRECATED: remove in 0.20.
|
||||
"environment_blacklist": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
# use exclude_env_vars instead
|
||||
"exclude_env_vars": {"type": "array", "default": [], "items": {"type": "string"}},
|
||||
"exclude_env_vars": {"type": "array", "default": [], "items": {"type": "string"}}
|
||||
},
|
||||
},
|
||||
"template": {"type": "string"},
|
||||
@ -80,11 +71,6 @@
|
||||
"properties": {
|
||||
"verbose": {"type": "boolean", "default": False},
|
||||
"hash_length": {"type": "integer", "minimum": 0, "default": 7},
|
||||
# DEPRECATED: remove in 0.20.
|
||||
"whitelist": array_of_strings,
|
||||
"blacklist": array_of_strings,
|
||||
"blacklist_implicits": {"type": "boolean", "default": False},
|
||||
# whitelist/blacklist have been replaced with include/exclude
|
||||
"include": array_of_strings,
|
||||
"exclude": array_of_strings,
|
||||
"exclude_implicits": {"type": "boolean", "default": False},
|
||||
@ -188,52 +174,3 @@
|
||||
"additionalProperties": False,
|
||||
"properties": properties,
|
||||
}
|
||||
|
||||
|
||||
# deprecated keys and their replacements
|
||||
old_to_new_key = {"exclude_implicits": "hide_implicits"}
|
||||
|
||||
|
||||
def update_keys(data, key_translations):
|
||||
"""Change blacklist/whitelist to exclude/include.
|
||||
|
||||
Arguments:
|
||||
data (dict): data from a valid modules configuration.
|
||||
key_translations (dict): A dictionary of keys to translate to
|
||||
their respective values.
|
||||
|
||||
Return:
|
||||
(bool) whether anything was changed in data
|
||||
"""
|
||||
changed = False
|
||||
|
||||
if isinstance(data, dict):
|
||||
keys = list(data.keys())
|
||||
for key in keys:
|
||||
value = data[key]
|
||||
|
||||
translation = key_translations.get(key)
|
||||
if translation:
|
||||
data[translation] = data.pop(key)
|
||||
changed = True
|
||||
|
||||
changed |= update_keys(value, key_translations)
|
||||
|
||||
elif isinstance(data, list):
|
||||
for elt in data:
|
||||
changed |= update_keys(elt, key_translations)
|
||||
|
||||
return changed
|
||||
|
||||
|
||||
def update(data):
|
||||
"""Update the data in place to remove deprecated properties.
|
||||
|
||||
Args:
|
||||
data (dict): dictionary to be updated
|
||||
|
||||
Returns:
|
||||
True if data was changed, False otherwise
|
||||
"""
|
||||
# translate blacklist/whitelist to exclude/include
|
||||
return update_keys(data, old_to_new_key)
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
import pprint
|
||||
import re
|
||||
import types
|
||||
import typing
|
||||
import warnings
|
||||
from typing import Callable, Dict, List, NamedTuple, Optional, Sequence, Set, Tuple, Union
|
||||
|
||||
@ -379,7 +380,7 @@ def check_packages_exist(specs):
|
||||
for spec in specs:
|
||||
for s in spec.traverse():
|
||||
try:
|
||||
check_passed = repo.exists(s.name) or repo.is_virtual(s.name)
|
||||
check_passed = repo.repo_for_pkg(s).exists(s.name) or repo.is_virtual(s.name)
|
||||
except Exception as e:
|
||||
msg = "Cannot find package: {0}".format(str(e))
|
||||
check_passed = False
|
||||
@ -713,7 +714,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 +813,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 +1014,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 +1118,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
|
||||
@ -1136,6 +1133,7 @@ def __init__(self, tests=False):
|
||||
|
||||
# Set during the call to setup
|
||||
self.pkgs = None
|
||||
self.explicitly_required_namespaces = {}
|
||||
|
||||
def pkg_version_rules(self, pkg):
|
||||
"""Output declared versions of a package.
|
||||
@ -1148,7 +1146,9 @@ def key_fn(version):
|
||||
# Origins are sorted by "provenance" first, see the Provenance enumeration above
|
||||
return version.origin, version.idx
|
||||
|
||||
pkg = packagize(pkg)
|
||||
if isinstance(pkg, str):
|
||||
pkg = self.pkg_class(pkg)
|
||||
|
||||
declared_versions = self.declared_versions[pkg.name]
|
||||
partially_sorted_versions = sorted(set(declared_versions), key=key_fn)
|
||||
|
||||
@ -1340,7 +1340,10 @@ def _rule_from_str(
|
||||
)
|
||||
|
||||
def pkg_rules(self, pkg, tests):
|
||||
pkg = packagize(pkg)
|
||||
pkg = self.pkg_class(pkg)
|
||||
|
||||
# Namespace of the package
|
||||
self.gen.fact(fn.pkg_fact(pkg.name, fn.namespace(pkg.namespace)))
|
||||
|
||||
# versions
|
||||
self.pkg_version_rules(pkg)
|
||||
@ -1518,7 +1521,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 +1529,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 +1545,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:
|
||||
@ -1673,9 +1676,10 @@ def provider_requirements(self):
|
||||
rules = self._rules_from_requirements(
|
||||
virtual_str, requirements, kind=RequirementKind.VIRTUAL
|
||||
)
|
||||
self.emit_facts_from_requirement_rules(rules)
|
||||
self.trigger_rules()
|
||||
self.effect_rules()
|
||||
if rules:
|
||||
self.emit_facts_from_requirement_rules(rules)
|
||||
self.trigger_rules()
|
||||
self.effect_rules()
|
||||
|
||||
def emit_facts_from_requirement_rules(self, rules: List[RequirementRule]):
|
||||
"""Generate facts to enforce requirements.
|
||||
@ -1802,15 +1806,12 @@ def external_packages(self):
|
||||
for local_idx, spec in enumerate(external_specs):
|
||||
msg = "%s available as external when satisfying %s" % (spec.name, spec)
|
||||
|
||||
def external_imposition(input_spec, _):
|
||||
return [fn.attr("external_conditions_hold", input_spec.name, local_idx)]
|
||||
def external_imposition(input_spec, requirements):
|
||||
return requirements + [
|
||||
fn.attr("external_conditions_hold", input_spec.name, local_idx)
|
||||
]
|
||||
|
||||
self.condition(
|
||||
spec,
|
||||
spack.spec.Spec(spec.name),
|
||||
msg=msg,
|
||||
transform_imposed=external_imposition,
|
||||
)
|
||||
self.condition(spec, spec, msg=msg, transform_imposed=external_imposition)
|
||||
self.possible_versions[spec.name].add(spec.version)
|
||||
self.gen.newline()
|
||||
|
||||
@ -1832,7 +1833,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))
|
||||
@ -1922,7 +1929,7 @@ class Head:
|
||||
node_flag = fn.attr("node_flag_set")
|
||||
node_flag_source = fn.attr("node_flag_source")
|
||||
node_flag_propagate = fn.attr("node_flag_propagate")
|
||||
variant_propagate = fn.attr("variant_propagate")
|
||||
variant_propagation_candidate = fn.attr("variant_propagation_candidate")
|
||||
|
||||
class Body:
|
||||
node = fn.attr("node")
|
||||
@ -1936,7 +1943,7 @@ class Body:
|
||||
node_flag = fn.attr("node_flag")
|
||||
node_flag_source = fn.attr("node_flag_source")
|
||||
node_flag_propagate = fn.attr("node_flag_propagate")
|
||||
variant_propagate = fn.attr("variant_propagate")
|
||||
variant_propagation_candidate = fn.attr("variant_propagation_candidate")
|
||||
|
||||
f = Body if body else Head
|
||||
|
||||
@ -1971,7 +1978,7 @@ class Body:
|
||||
if not spec.concrete:
|
||||
reserved_names = spack.directives.reserved_names
|
||||
if not spec.virtual and vname not in reserved_names:
|
||||
pkg_cls = spack.repo.PATH.get_pkg_class(spec.name)
|
||||
pkg_cls = self.pkg_class(spec.name)
|
||||
try:
|
||||
variant_def, _ = pkg_cls.variants[vname]
|
||||
except KeyError:
|
||||
@ -1985,7 +1992,9 @@ class Body:
|
||||
clauses.append(f.variant_value(spec.name, vname, value))
|
||||
|
||||
if variant.propagate:
|
||||
clauses.append(f.variant_propagate(spec.name, vname, value, spec.name))
|
||||
clauses.append(
|
||||
f.variant_propagation_candidate(spec.name, vname, value, spec.name)
|
||||
)
|
||||
|
||||
# Tell the concretizer that this is a possible value for the
|
||||
# variant, to account for things like int/str values where we
|
||||
@ -2088,7 +2097,7 @@ def define_package_versions_and_validate_preferences(
|
||||
"""Declare any versions in specs not declared in packages."""
|
||||
packages_yaml = spack.config.get("packages")
|
||||
for pkg_name in possible_pkgs:
|
||||
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
|
||||
pkg_cls = self.pkg_class(pkg_name)
|
||||
|
||||
# All the versions from the corresponding package.py file. Since concepts
|
||||
# like being a "develop" version or being preferred exist only at a
|
||||
@ -2176,7 +2185,7 @@ def _supported_targets(self, compiler_name, compiler_version, targets):
|
||||
try:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
target.optimization_flags(compiler_name, compiler_version)
|
||||
target.optimization_flags(compiler_name, str(compiler_version))
|
||||
supported.append(target)
|
||||
except archspec.cpu.UnsupportedMicroarchitecture:
|
||||
continue
|
||||
@ -2548,14 +2557,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)
|
||||
self.possible_virtuals = node_counter.possible_virtuals()
|
||||
self.pkgs = node_counter.possible_dependencies()
|
||||
@ -2568,6 +2571,10 @@ def setup(
|
||||
if missing_deps:
|
||||
raise spack.spec.InvalidDependencyError(spec.name, missing_deps)
|
||||
|
||||
for node in spack.traverse.traverse_nodes(specs):
|
||||
if node.namespace is not None:
|
||||
self.explicitly_required_namespaces[node.name] = node.namespace
|
||||
|
||||
# driver is used by all the functions below to add facts and
|
||||
# rules to generate an ASP program.
|
||||
self.gen = driver
|
||||
@ -2673,23 +2680,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]
|
||||
@ -2779,6 +2784,13 @@ def _specs_from_requires(self, pkg_name, section):
|
||||
for s in spec_group[key]:
|
||||
yield _spec_with_default_name(s, pkg_name)
|
||||
|
||||
def pkg_class(self, pkg_name: str) -> typing.Type["spack.package_base.PackageBase"]:
|
||||
request = pkg_name
|
||||
if pkg_name in self.explicitly_required_namespaces:
|
||||
namespace = self.explicitly_required_namespaces[pkg_name]
|
||||
request = f"{namespace}.{pkg_name}"
|
||||
return spack.repo.PATH.get_pkg_class(request)
|
||||
|
||||
|
||||
class SpecBuilder:
|
||||
"""Class with actions to rebuild a spec from ASP results."""
|
||||
@ -2790,9 +2802,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$",
|
||||
@ -2836,6 +2850,9 @@ def _arch(self, node):
|
||||
self._specs[node].architecture = arch
|
||||
return arch
|
||||
|
||||
def namespace(self, node, namespace):
|
||||
self._specs[node].namespace = namespace
|
||||
|
||||
def node_platform(self, node, platform):
|
||||
self._arch(node).platform = platform
|
||||
|
||||
@ -3050,14 +3067,6 @@ def build_specs(self, function_tuples):
|
||||
|
||||
action(*args)
|
||||
|
||||
# namespace assignment is done after the fact, as it is not
|
||||
# currently part of the solve
|
||||
for spec in self._specs.values():
|
||||
if spec.namespace:
|
||||
continue
|
||||
repo = spack.repo.PATH.repo_for_pkg(spec)
|
||||
spec.namespace = repo.namespace
|
||||
|
||||
# fix flags after all specs are constructed
|
||||
self.reorder_flags()
|
||||
|
||||
|
@ -45,6 +45,9 @@
|
||||
:- attr("depends_on", node(min_dupe_id, Package), node(ID, _), "link"), ID != min_dupe_id, unification_set("root", node(min_dupe_id, Package)), internal_error("link dependency out of the root unification set").
|
||||
:- attr("depends_on", node(min_dupe_id, Package), node(ID, _), "run"), ID != min_dupe_id, unification_set("root", node(min_dupe_id, Package)), internal_error("run dependency out of the root unification set").
|
||||
|
||||
% Namespaces are statically assigned by a package fact
|
||||
attr("namespace", node(ID, Package), Namespace) :- attr("node", node(ID, Package)), pkg_fact(Package, namespace(Namespace)).
|
||||
|
||||
% Rules on "unification sets", i.e. on sets of nodes allowing a single configuration of any given package
|
||||
unify(SetID, PackageName) :- unification_set(SetID, node(_, PackageName)).
|
||||
:- 2 { unification_set(SetID, node(_, PackageName)) }, unify(SetID, PackageName).
|
||||
@ -695,6 +698,26 @@ requirement_group_satisfied(node(ID, Package), X) :-
|
||||
activate_requirement(node(ID, Package), X),
|
||||
requirement_group(Package, X).
|
||||
|
||||
% Do not impose requirements, if the conditional requirement is not active
|
||||
do_not_impose(EffectID, node(ID, Package)) :-
|
||||
trigger_condition_holds(TriggerID, node(ID, Package)),
|
||||
pkg_fact(Package, condition_trigger(ConditionID, TriggerID)),
|
||||
pkg_fact(Package, condition_effect(ConditionID, EffectID)),
|
||||
requirement_group_member(ConditionID , Package, RequirementID),
|
||||
not activate_requirement(node(ID, Package), RequirementID).
|
||||
|
||||
% When we have a required provider, we need to ensure that the provider/2 facts respect
|
||||
% the requirement. This is particularly important for packages that could provide multiple
|
||||
% virtuals independently
|
||||
required_provider(Provider, Virtual)
|
||||
:- requirement_group_member(ConditionID, Virtual, RequirementID),
|
||||
condition_holds(ConditionID, _),
|
||||
virtual(Virtual),
|
||||
pkg_fact(Virtual, condition_effect(ConditionID, EffectID)),
|
||||
imposed_constraint(EffectID, "node", Provider).
|
||||
|
||||
:- provider(node(Y, Package), node(X, Virtual)), required_provider(Provider, Virtual), Package != Provider.
|
||||
|
||||
% TODO: the following two choice rules allow the solver to add compiler
|
||||
% flags if their only source is from a requirement. This is overly-specific
|
||||
% and should use a more-generic approach like in https://github.com/spack/spack/pull/37180
|
||||
@ -757,23 +780,36 @@ node_has_variant(node(ID, Package), Variant) :-
|
||||
pkg_fact(Package, variant(Variant)),
|
||||
attr("node", node(ID, Package)).
|
||||
|
||||
attr("variant_propagate", PackageNode, Variant, Value, Source) :-
|
||||
% Variant propagation is forwarded to dependencies
|
||||
attr("variant_propagation_candidate", PackageNode, Variant, Value, Source) :-
|
||||
attr("node", PackageNode),
|
||||
depends_on(ParentNode, PackageNode),
|
||||
attr("variant_propagate", ParentNode, Variant, Value, Source),
|
||||
not attr("variant_set", PackageNode, Variant).
|
||||
attr("variant_value", node(_, Source), Variant, Value),
|
||||
attr("variant_propagation_candidate", ParentNode, Variant, _, Source).
|
||||
|
||||
attr("variant_value", node(ID, Package), Variant, Value) :-
|
||||
attr("node", node(ID, Package)),
|
||||
% If the node is a candidate, and it has the variant and value,
|
||||
% then those variant and value should be propagated
|
||||
attr("variant_propagate", node(ID, Package), Variant, Value, Source) :-
|
||||
attr("variant_propagation_candidate", node(ID, Package), Variant, Value, Source),
|
||||
node_has_variant(node(ID, Package), Variant),
|
||||
attr("variant_propagate", node(ID, Package), Variant, Value, _),
|
||||
pkg_fact(Package, variant_possible_value(Variant, Value)).
|
||||
pkg_fact(Package, variant_possible_value(Variant, Value)),
|
||||
not attr("variant_set", node(ID, Package), Variant).
|
||||
|
||||
% Propagate the value, if there is the corresponding attribute
|
||||
attr("variant_value", PackageNode, Variant, Value) :- attr("variant_propagate", PackageNode, Variant, Value, _).
|
||||
|
||||
% If a variant is propagated, we cannot have extraneous values (this is for multi valued variants)
|
||||
variant_is_propagated(PackageNode, Variant) :- attr("variant_propagate", PackageNode, Variant, _, _).
|
||||
:- variant_is_propagated(PackageNode, Variant),
|
||||
attr("variant_value", PackageNode, Variant, Value),
|
||||
not attr("variant_propagate", PackageNode, Variant, Value, _).
|
||||
|
||||
% Cannot receive different values from different sources on the same variant
|
||||
error(100, "{0} and {1} cannot both propagate variant '{2}' to package {3} with values '{4}' and '{5}'", Source1, Source2, Variant, Package, Value1, Value2) :-
|
||||
attr("variant_propagate", node(X, Package), Variant, Value1, Source1),
|
||||
attr("variant_propagate", node(X, Package), Variant, Value2, Source2),
|
||||
node_has_variant(node(X, Package), Variant),
|
||||
Value1 < Value2.
|
||||
Value1 < Value2, Source1 < Source2.
|
||||
|
||||
% a variant cannot be set if it is not a variant on the package
|
||||
error(100, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Variant, Package)
|
||||
|
@ -10,6 +10,9 @@
|
||||
%=============================================================================
|
||||
|
||||
% macOS
|
||||
os_compatible("sequoia", "sonoma").
|
||||
os_compatible("sonoma", "ventura").
|
||||
os_compatible("ventura", "monterey").
|
||||
os_compatible("monterey", "bigsur").
|
||||
os_compatible("bigsur", "catalina").
|
||||
|
||||
|
@ -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", {})
|
||||
|
||||
|
@ -102,7 +102,10 @@ def to_dict_or_value(self):
|
||||
if self.microarchitecture.vendor == "generic":
|
||||
return str(self)
|
||||
|
||||
return syaml.syaml_dict(self.microarchitecture.to_dict(return_list_of_items=True))
|
||||
# Get rid of compiler flag information before turning the uarch into a dict
|
||||
uarch_dict = self.microarchitecture.to_dict()
|
||||
uarch_dict.pop("compilers", None)
|
||||
return syaml.syaml_dict(uarch_dict.items())
|
||||
|
||||
def __repr__(self):
|
||||
cls_name = self.__class__.__name__
|
||||
|
@ -8,13 +8,16 @@
|
||||
|
||||
import pytest
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import llnl.util.filesystem as fs
|
||||
|
||||
import spack.compilers
|
||||
import spack.concretize
|
||||
import spack.operating_systems
|
||||
import spack.platforms
|
||||
import spack.target
|
||||
from spack.spec import ArchSpec, CompilerSpec, Spec
|
||||
from spack.spec import ArchSpec, Spec
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@ -121,52 +124,60 @@ def test_arch_spec_container_semantic(item, architecture_str):
|
||||
@pytest.mark.parametrize(
|
||||
"compiler_spec,target_name,expected_flags",
|
||||
[
|
||||
# Check compilers with version numbers from a single toolchain
|
||||
# Homogeneous compilers
|
||||
("gcc@4.7.2", "ivybridge", "-march=core-avx-i -mtune=core-avx-i"),
|
||||
# Check mixed toolchains
|
||||
("clang@8.0.0", "broadwell", ""),
|
||||
("clang@3.5", "x86_64", "-march=x86-64 -mtune=generic"),
|
||||
# Check Apple's Clang compilers
|
||||
("apple-clang@9.1.0", "x86_64", "-march=x86-64"),
|
||||
# Mixed toolchain
|
||||
("clang@8.0.0", "broadwell", ""),
|
||||
],
|
||||
)
|
||||
@pytest.mark.filterwarnings("ignore:microarchitecture specific")
|
||||
def test_optimization_flags(compiler_spec, target_name, expected_flags, config):
|
||||
def test_optimization_flags(compiler_spec, target_name, expected_flags, compiler_factory):
|
||||
target = spack.target.Target(target_name)
|
||||
compiler = spack.compilers.compilers_for_spec(compiler_spec).pop()
|
||||
compiler_dict = compiler_factory(spec=compiler_spec, operating_system="")["compiler"]
|
||||
if compiler_spec == "clang@8.0.0":
|
||||
compiler_dict["paths"] = {
|
||||
"cc": "/path/to/clang-8",
|
||||
"cxx": "/path/to/clang++-8",
|
||||
"f77": "/path/to/gfortran-9",
|
||||
"fc": "/path/to/gfortran-9",
|
||||
}
|
||||
compiler = spack.compilers.compiler_from_dict(compiler_dict)
|
||||
|
||||
opt_flags = target.optimization_flags(compiler)
|
||||
assert opt_flags == expected_flags
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"compiler,real_version,target_str,expected_flags",
|
||||
"compiler_str,real_version,target_str,expected_flags",
|
||||
[
|
||||
(CompilerSpec("gcc@=9.2.0"), None, "haswell", "-march=haswell -mtune=haswell"),
|
||||
("gcc@=9.2.0", None, "haswell", "-march=haswell -mtune=haswell"),
|
||||
# Check that custom string versions are accepted
|
||||
(
|
||||
CompilerSpec("gcc@=10foo"),
|
||||
"9.2.0",
|
||||
"icelake",
|
||||
"-march=icelake-client -mtune=icelake-client",
|
||||
),
|
||||
("gcc@=10foo", "9.2.0", "icelake", "-march=icelake-client -mtune=icelake-client"),
|
||||
# Check that we run version detection (4.4.0 doesn't support icelake)
|
||||
(
|
||||
CompilerSpec("gcc@=4.4.0-special"),
|
||||
"9.2.0",
|
||||
"icelake",
|
||||
"-march=icelake-client -mtune=icelake-client",
|
||||
),
|
||||
("gcc@=4.4.0-special", "9.2.0", "icelake", "-march=icelake-client -mtune=icelake-client"),
|
||||
# Check that the special case for Apple's clang is treated correctly
|
||||
# i.e. it won't try to detect the version again
|
||||
(CompilerSpec("apple-clang@=9.1.0"), None, "x86_64", "-march=x86-64"),
|
||||
("apple-clang@=9.1.0", None, "x86_64", "-march=x86-64"),
|
||||
],
|
||||
)
|
||||
def test_optimization_flags_with_custom_versions(
|
||||
compiler, real_version, target_str, expected_flags, monkeypatch, config
|
||||
compiler_str,
|
||||
real_version,
|
||||
target_str,
|
||||
expected_flags,
|
||||
monkeypatch,
|
||||
mutable_config,
|
||||
compiler_factory,
|
||||
):
|
||||
target = spack.target.Target(target_str)
|
||||
compiler_dict = compiler_factory(spec=compiler_str, operating_system="redhat6")
|
||||
mutable_config.set("compilers", [compiler_dict])
|
||||
if real_version:
|
||||
monkeypatch.setattr(spack.compiler.Compiler, "get_real_version", lambda x: real_version)
|
||||
compiler = spack.compilers.compiler_from_dict(compiler_dict["compiler"])
|
||||
|
||||
opt_flags = target.optimization_flags(compiler)
|
||||
assert opt_flags == expected_flags
|
||||
|
||||
@ -201,13 +212,16 @@ def test_satisfy_strict_constraint_when_not_concrete(architecture_tuple, constra
|
||||
)
|
||||
@pytest.mark.usefixtures("mock_packages", "config")
|
||||
@pytest.mark.only_clingo("Fixing the parser broke this test for the original concretizer.")
|
||||
@pytest.mark.skipif(
|
||||
str(archspec.cpu.host().family) != "x86_64", reason="tests are for x86_64 uarch ranges"
|
||||
)
|
||||
def test_concretize_target_ranges(root_target_range, dep_target_range, result, monkeypatch):
|
||||
# Monkeypatch so that all concretization is done as if the machine is core2
|
||||
monkeypatch.setattr(spack.platforms.test.Test, "default", "core2")
|
||||
spec = Spec(f"a %gcc@10 foobar=bar target={root_target_range} ^b target={dep_target_range}")
|
||||
spec = Spec(
|
||||
f"pkg-a %gcc@10 foobar=bar target={root_target_range} ^pkg-b target={dep_target_range}"
|
||||
)
|
||||
with spack.concretize.disable_compiler_existence_check():
|
||||
spec.concretize()
|
||||
assert spec.target == spec["b"].target == result
|
||||
assert spec.target == spec["pkg-b"].target == result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -4,7 +4,9 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
import filecmp
|
||||
import glob
|
||||
import gzip
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
@ -17,6 +19,8 @@
|
||||
import py
|
||||
import pytest
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
from llnl.util.filesystem import join_path, visit_directory_tree
|
||||
|
||||
import spack.binary_distribution as bindist
|
||||
@ -199,6 +203,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)
|
||||
|
||||
@ -568,11 +575,20 @@ def test_update_sbang(tmpdir, test_mirror):
|
||||
uninstall_cmd("-y", "/%s" % new_spec.dag_hash())
|
||||
|
||||
|
||||
def test_install_legacy_buildcache_layout(install_mockery_mutable_config):
|
||||
@pytest.mark.skipif(
|
||||
str(archspec.cpu.host().family) != "x86_64",
|
||||
reason="test data uses gcc 4.5.0 which does not support aarch64",
|
||||
)
|
||||
def test_install_legacy_buildcache_layout(
|
||||
mutable_config, compiler_factory, install_mockery_mutable_config
|
||||
):
|
||||
"""Legacy buildcache layout involved a nested archive structure
|
||||
where the .spack file contained a repeated spec.json and another
|
||||
compressed archive file containing the install tree. This test
|
||||
makes sure we can still read that layout."""
|
||||
mutable_config.set(
|
||||
"compilers", [compiler_factory(spec="gcc@4.5.0", operating_system="debian6")]
|
||||
)
|
||||
legacy_layout_dir = os.path.join(test_path, "data", "mirrors", "legacy_layout")
|
||||
mirror_url = "file://{0}".format(legacy_layout_dir)
|
||||
filename = (
|
||||
@ -1022,7 +1038,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"}
|
||||
@ -1042,13 +1060,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"))
|
||||
@ -1112,3 +1147,77 @@ def test_tarfile_of_spec_prefix(tmpdir):
|
||||
assert tar.getmember(f"{expected_prefix}/b_directory/file").isreg()
|
||||
assert tar.getmember(f"{expected_prefix}/c_directory").isdir()
|
||||
assert tar.getmember(f"{expected_prefix}/c_directory/file").isreg()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("layout,expect_success", [(None, True), (1, True), (2, False)])
|
||||
def test_get_valid_spec_file(tmp_path, layout, expect_success):
|
||||
# Test reading a spec.json file that does not specify a layout version.
|
||||
spec_dict = Spec("example").to_dict()
|
||||
path = tmp_path / "spec.json"
|
||||
effective_layout = layout or 0 # If not specified it should be 0
|
||||
|
||||
# Add a layout version
|
||||
if layout is not None:
|
||||
spec_dict["buildcache_layout_version"] = layout
|
||||
|
||||
# Save to file
|
||||
with open(path, "w") as f:
|
||||
json.dump(spec_dict, f)
|
||||
|
||||
try:
|
||||
spec_dict_disk, layout_disk = bindist._get_valid_spec_file(
|
||||
str(path), max_supported_layout=1
|
||||
)
|
||||
assert expect_success
|
||||
assert spec_dict_disk == spec_dict
|
||||
assert layout_disk == effective_layout
|
||||
except bindist.InvalidMetadataFile:
|
||||
assert not expect_success
|
||||
|
||||
|
||||
def test_get_valid_spec_file_doesnt_exist(tmp_path):
|
||||
with pytest.raises(bindist.InvalidMetadataFile, match="No such file"):
|
||||
bindist._get_valid_spec_file(str(tmp_path / "no-such-file"), max_supported_layout=1)
|
||||
|
||||
|
||||
def test_get_valid_spec_file_gzipped(tmp_path):
|
||||
# Create a gzipped file, contents don't matter
|
||||
path = tmp_path / "spec.json.gz"
|
||||
with gzip.open(path, "wb") as f:
|
||||
f.write(b"hello")
|
||||
with pytest.raises(
|
||||
bindist.InvalidMetadataFile, match="Compressed spec files are not supported"
|
||||
):
|
||||
bindist._get_valid_spec_file(str(path), max_supported_layout=1)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("filename", ["spec.json", "spec.json.sig"])
|
||||
def test_get_valid_spec_file_no_json(tmp_path, filename):
|
||||
tmp_path.joinpath(filename).write_text("not json")
|
||||
with pytest.raises(bindist.InvalidMetadataFile):
|
||||
bindist._get_valid_spec_file(str(tmp_path / filename), max_supported_layout=1)
|
||||
|
||||
|
||||
def test_download_tarball_with_unsupported_layout_fails(tmp_path, mutable_config, capsys):
|
||||
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()
|
||||
spec_dict["buildcache_layout_version"] = layout_version
|
||||
|
||||
# Setup a basic local build cache structure
|
||||
path = (
|
||||
tmp_path / bindist.build_cache_relative_path() / bindist.tarball_name(spec, ".spec.json")
|
||||
)
|
||||
path.parent.mkdir(parents=True)
|
||||
with open(path, "w") as f:
|
||||
json.dump(spec_dict, f)
|
||||
|
||||
# Configure as a mirror.
|
||||
mirror_cmd("add", "test-mirror", str(tmp_path))
|
||||
|
||||
# Shouldn't be able "download" this.
|
||||
assert bindist.download_tarball(spec, unsigned=True) is None
|
||||
|
||||
# And there should be a warning about an unsupported layout version.
|
||||
assert f"Layout version {layout_version} is too new" in capsys.readouterr().err
|
||||
|
@ -437,14 +437,14 @@ def test_parallel_false_is_not_propagating(default_mock_concretization):
|
||||
# a foobar=bar (parallel = False)
|
||||
# |
|
||||
# b (parallel =True)
|
||||
s = default_mock_concretization("a foobar=bar")
|
||||
s = default_mock_concretization("pkg-a foobar=bar")
|
||||
|
||||
spack.build_environment.set_package_py_globals(s.package)
|
||||
assert s["a"].package.module.make_jobs == 1
|
||||
assert s["pkg-a"].package.module.make_jobs == 1
|
||||
|
||||
spack.build_environment.set_package_py_globals(s["b"].package)
|
||||
assert s["b"].package.module.make_jobs == spack.build_environment.determine_number_of_jobs(
|
||||
parallel=s["b"].package.parallel
|
||||
spack.build_environment.set_package_py_globals(s["pkg-b"].package)
|
||||
assert s["pkg-b"].package.module.make_jobs == spack.build_environment.determine_number_of_jobs(
|
||||
parallel=s["pkg-b"].package.parallel
|
||||
)
|
||||
|
||||
|
||||
@ -540,7 +540,7 @@ def test_dirty_disable_module_unload(config, mock_packages, working_env, mock_mo
|
||||
"""Test that on CRAY platform 'module unload' is not called if the 'dirty'
|
||||
option is on.
|
||||
"""
|
||||
s = spack.spec.Spec("a").concretized()
|
||||
s = spack.spec.Spec("pkg-a").concretized()
|
||||
|
||||
# If called with "dirty" we don't unload modules, so no calls to the
|
||||
# `module` function on Cray
|
||||
|
@ -9,6 +9,8 @@
|
||||
import py.path
|
||||
import pytest
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import llnl.util.filesystem as fs
|
||||
|
||||
import spack.build_systems.autotools
|
||||
@ -95,7 +97,7 @@ def test_negative_ninja_check(self, input_dir, test_dir, concretize_and_setup):
|
||||
@pytest.mark.usefixtures("config", "mock_packages")
|
||||
class TestAutotoolsPackage:
|
||||
def test_with_or_without(self, default_mock_concretization):
|
||||
s = default_mock_concretization("a")
|
||||
s = default_mock_concretization("pkg-a")
|
||||
options = s.package.with_or_without("foo")
|
||||
|
||||
# Ensure that values that are not representing a feature
|
||||
@ -127,7 +129,7 @@ def activate(value):
|
||||
assert "--without-lorem-ipsum" in options
|
||||
|
||||
def test_none_is_allowed(self, default_mock_concretization):
|
||||
s = default_mock_concretization("a foo=none")
|
||||
s = default_mock_concretization("pkg-a foo=none")
|
||||
options = s.package.with_or_without("foo")
|
||||
|
||||
# Ensure that values that are not representing a feature
|
||||
@ -209,6 +211,9 @@ def test_autotools_gnuconfig_replacement_disabled(
|
||||
assert "gnuconfig version of config.guess" not in f.read()
|
||||
|
||||
@pytest.mark.disable_clean_stage_check
|
||||
@pytest.mark.skipif(
|
||||
str(archspec.cpu.host().family) != "x86_64", reason="test data is specific for x86_64"
|
||||
)
|
||||
def test_autotools_gnuconfig_replacement_no_gnuconfig(self, mutable_database, monkeypatch):
|
||||
"""
|
||||
Tests whether a useful error message is shown when patch_config_files is
|
||||
|
@ -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():
|
||||
|
@ -102,24 +102,24 @@ def test_specs_staging(config, tmpdir):
|
||||
|
||||
"""
|
||||
builder = repo.MockRepositoryBuilder(tmpdir)
|
||||
builder.add_package("g")
|
||||
builder.add_package("f")
|
||||
builder.add_package("e")
|
||||
builder.add_package("d", dependencies=[("f", None, None), ("g", None, None)])
|
||||
builder.add_package("c")
|
||||
builder.add_package("b", dependencies=[("d", None, None), ("e", None, None)])
|
||||
builder.add_package("a", dependencies=[("b", None, None), ("c", None, None)])
|
||||
builder.add_package("pkg-g")
|
||||
builder.add_package("pkg-f")
|
||||
builder.add_package("pkg-e")
|
||||
builder.add_package("pkg-d", dependencies=[("pkg-f", None, None), ("pkg-g", None, None)])
|
||||
builder.add_package("pkg-c")
|
||||
builder.add_package("pkg-b", dependencies=[("pkg-d", None, None), ("pkg-e", None, None)])
|
||||
builder.add_package("pkg-a", dependencies=[("pkg-b", None, None), ("pkg-c", None, None)])
|
||||
|
||||
with repo.use_repositories(builder.root):
|
||||
spec_a = Spec("a").concretized()
|
||||
spec_a = Spec("pkg-a").concretized()
|
||||
|
||||
spec_a_label = ci._spec_deps_key(spec_a)
|
||||
spec_b_label = ci._spec_deps_key(spec_a["b"])
|
||||
spec_c_label = ci._spec_deps_key(spec_a["c"])
|
||||
spec_d_label = ci._spec_deps_key(spec_a["d"])
|
||||
spec_e_label = ci._spec_deps_key(spec_a["e"])
|
||||
spec_f_label = ci._spec_deps_key(spec_a["f"])
|
||||
spec_g_label = ci._spec_deps_key(spec_a["g"])
|
||||
spec_b_label = ci._spec_deps_key(spec_a["pkg-b"])
|
||||
spec_c_label = ci._spec_deps_key(spec_a["pkg-c"])
|
||||
spec_d_label = ci._spec_deps_key(spec_a["pkg-d"])
|
||||
spec_e_label = ci._spec_deps_key(spec_a["pkg-e"])
|
||||
spec_f_label = ci._spec_deps_key(spec_a["pkg-f"])
|
||||
spec_g_label = ci._spec_deps_key(spec_a["pkg-g"])
|
||||
|
||||
spec_labels, dependencies, stages = ci.stage_spec_jobs([spec_a])
|
||||
|
||||
@ -1256,7 +1256,7 @@ def test_ci_generate_override_runner_attrs(
|
||||
spack:
|
||||
specs:
|
||||
- flatten-deps
|
||||
- a
|
||||
- pkg-a
|
||||
mirrors:
|
||||
some-mirror: https://my.fake.mirror
|
||||
ci:
|
||||
@ -1273,12 +1273,12 @@ def test_ci_generate_override_runner_attrs(
|
||||
- match:
|
||||
- dependency-install
|
||||
- match:
|
||||
- a
|
||||
- pkg-a
|
||||
build-job:
|
||||
tags:
|
||||
- specific-a-2
|
||||
- match:
|
||||
- a
|
||||
- pkg-a
|
||||
build-job-remove:
|
||||
tags:
|
||||
- toplevel2
|
||||
@ -1338,8 +1338,8 @@ def test_ci_generate_override_runner_attrs(
|
||||
assert global_vars["SPACK_CHECKOUT_VERSION"] == git_version or "v0.20.0.test0"
|
||||
|
||||
for ci_key in yaml_contents.keys():
|
||||
if ci_key.startswith("a"):
|
||||
# Make sure a's attributes override variables, and all the
|
||||
if ci_key.startswith("pkg-a"):
|
||||
# Make sure pkg-a's attributes override variables, and all the
|
||||
# scripts. Also, make sure the 'toplevel' tag doesn't
|
||||
# appear twice, but that a's specific extra tag does appear
|
||||
the_elt = yaml_contents[ci_key]
|
||||
@ -1798,7 +1798,7 @@ def test_ci_generate_read_broken_specs_url(
|
||||
tmpdir, mutable_mock_env_path, install_mockery, mock_packages, monkeypatch, ci_base_environment
|
||||
):
|
||||
"""Verify that `broken-specs-url` works as intended"""
|
||||
spec_a = Spec("a")
|
||||
spec_a = Spec("pkg-a")
|
||||
spec_a.concretize()
|
||||
a_dag_hash = spec_a.dag_hash()
|
||||
|
||||
@ -1824,7 +1824,7 @@ def test_ci_generate_read_broken_specs_url(
|
||||
spack:
|
||||
specs:
|
||||
- flatten-deps
|
||||
- a
|
||||
- pkg-a
|
||||
mirrors:
|
||||
some-mirror: https://my.fake.mirror
|
||||
ci:
|
||||
@ -1832,9 +1832,9 @@ def test_ci_generate_read_broken_specs_url(
|
||||
pipeline-gen:
|
||||
- submapping:
|
||||
- match:
|
||||
- a
|
||||
- pkg-a
|
||||
- flatten-deps
|
||||
- b
|
||||
- pkg-b
|
||||
- dependency-install
|
||||
build-job:
|
||||
tags:
|
||||
@ -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:
|
||||
|
@ -81,14 +81,14 @@ def test_match_spec_env(mock_packages, mutable_mock_env_path):
|
||||
"""
|
||||
# Initial sanity check: we are planning on choosing a non-default
|
||||
# value, so make sure that is in fact not the default.
|
||||
check_defaults = spack.cmd.parse_specs(["a"], concretize=True)[0]
|
||||
check_defaults = spack.cmd.parse_specs(["pkg-a"], concretize=True)[0]
|
||||
assert not check_defaults.satisfies("foobar=baz")
|
||||
|
||||
e = ev.create("test")
|
||||
e.add("a foobar=baz")
|
||||
e.add("pkg-a foobar=baz")
|
||||
e.concretize()
|
||||
with e:
|
||||
env_spec = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["a"])[0])
|
||||
env_spec = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["pkg-a"])[0])
|
||||
assert env_spec.satisfies("foobar=baz")
|
||||
assert env_spec.concrete
|
||||
|
||||
@ -96,12 +96,12 @@ def test_match_spec_env(mock_packages, mutable_mock_env_path):
|
||||
@pytest.mark.usefixtures("config")
|
||||
def test_multiple_env_match_raises_error(mock_packages, mutable_mock_env_path):
|
||||
e = ev.create("test")
|
||||
e.add("a foobar=baz")
|
||||
e.add("a foobar=fee")
|
||||
e.add("pkg-a foobar=baz")
|
||||
e.add("pkg-a foobar=fee")
|
||||
e.concretize()
|
||||
with e:
|
||||
with pytest.raises(ev.SpackEnvironmentError) as exc_info:
|
||||
spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["a"])[0])
|
||||
spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["pkg-a"])[0])
|
||||
|
||||
assert "matches multiple specs" in exc_info.value.message
|
||||
|
||||
@ -109,16 +109,16 @@ def test_multiple_env_match_raises_error(mock_packages, mutable_mock_env_path):
|
||||
@pytest.mark.usefixtures("config")
|
||||
def test_root_and_dep_match_returns_root(mock_packages, mutable_mock_env_path):
|
||||
e = ev.create("test")
|
||||
e.add("b@0.9")
|
||||
e.add("a foobar=bar") # Depends on b, should choose b@1.0
|
||||
e.add("pkg-b@0.9")
|
||||
e.add("pkg-a foobar=bar") # Depends on b, should choose b@1.0
|
||||
e.concretize()
|
||||
with e:
|
||||
# This query matches the root b and b as a dependency of a. In that
|
||||
# case the root instance should be preferred.
|
||||
env_spec1 = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["b"])[0])
|
||||
env_spec1 = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["pkg-b"])[0])
|
||||
assert env_spec1.satisfies("@0.9")
|
||||
|
||||
env_spec2 = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["b@1.0"])[0])
|
||||
env_spec2 = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["pkg-b@1.0"])[0])
|
||||
assert env_spec2
|
||||
|
||||
|
||||
|
@ -111,10 +111,10 @@ def test_compiler_find_no_apple_gcc(no_compilers_yaml, working_env, mock_executa
|
||||
@pytest.mark.regression("37996")
|
||||
def test_compiler_remove(mutable_config, mock_packages):
|
||||
"""Tests that we can remove a compiler from configuration."""
|
||||
assert spack.spec.CompilerSpec("gcc@=4.5.0") in spack.compilers.all_compiler_specs()
|
||||
args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@4.5.0", add_paths=[], scope=None)
|
||||
assert spack.spec.CompilerSpec("gcc@=4.8.0") in spack.compilers.all_compiler_specs()
|
||||
args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@4.8.0", add_paths=[], scope=None)
|
||||
spack.cmd.compiler.compiler_remove(args)
|
||||
assert spack.spec.CompilerSpec("gcc@=4.5.0") not in spack.compilers.all_compiler_specs()
|
||||
assert spack.spec.CompilerSpec("gcc@=4.8.0") not in spack.compilers.all_compiler_specs()
|
||||
|
||||
|
||||
@pytest.mark.regression("37996")
|
||||
@ -123,10 +123,10 @@ def test_removing_compilers_from_multiple_scopes(mutable_config, mock_packages):
|
||||
site_config = spack.config.get("compilers", scope="site")
|
||||
spack.config.set("compilers", site_config, scope="user")
|
||||
|
||||
assert spack.spec.CompilerSpec("gcc@=4.5.0") in spack.compilers.all_compiler_specs()
|
||||
args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@4.5.0", add_paths=[], scope=None)
|
||||
assert spack.spec.CompilerSpec("gcc@=4.8.0") in spack.compilers.all_compiler_specs()
|
||||
args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@4.8.0", add_paths=[], scope=None)
|
||||
spack.cmd.compiler.compiler_remove(args)
|
||||
assert spack.spec.CompilerSpec("gcc@=4.5.0") not in spack.compilers.all_compiler_specs()
|
||||
assert spack.spec.CompilerSpec("gcc@=4.8.0") not in spack.compilers.all_compiler_specs()
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("Cannot execute bash script on Windows")
|
||||
|
@ -51,8 +51,8 @@ def test_concretize_root_test_dependencies_are_concretized(unify, mutable_mock_e
|
||||
|
||||
with ev.read("test") as e:
|
||||
e.unify = unify
|
||||
add("a")
|
||||
add("b")
|
||||
add("pkg-a")
|
||||
add("pkg-b")
|
||||
concretize("--test", "root")
|
||||
assert e.matching_spec("test-dependency")
|
||||
|
||||
|
78
lib/spack/spack/test/cmd/deconcretize.py
Normal file
78
lib/spack/spack/test/cmd/deconcretize.py
Normal file
@ -0,0 +1,78 @@
|
||||
# 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)
|
||||
|
||||
import pytest
|
||||
|
||||
import spack.environment as ev
|
||||
from spack.main import SpackCommand, SpackCommandError
|
||||
|
||||
deconcretize = SpackCommand("deconcretize")
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def test_env(mutable_mock_env_path, config, mock_packages):
|
||||
ev.create("test")
|
||||
with ev.read("test") as e:
|
||||
e.add("pkg-a@2.0 foobar=bar ^pkg-b@1.0")
|
||||
e.add("pkg-a@1.0 foobar=bar ^pkg-b@0.9")
|
||||
e.concretize()
|
||||
e.write()
|
||||
|
||||
|
||||
def test_deconcretize_dep(test_env):
|
||||
with ev.read("test") as e:
|
||||
deconcretize("-y", "pkg-b@1.0")
|
||||
specs = [s for s, _ in e.concretized_specs()]
|
||||
|
||||
assert len(specs) == 1
|
||||
assert specs[0].satisfies("pkg-a@1.0")
|
||||
|
||||
|
||||
def test_deconcretize_all_dep(test_env):
|
||||
with ev.read("test") as e:
|
||||
with pytest.raises(SpackCommandError):
|
||||
deconcretize("-y", "pkg-b")
|
||||
deconcretize("-y", "--all", "pkg-b")
|
||||
specs = [s for s, _ in e.concretized_specs()]
|
||||
|
||||
assert len(specs) == 0
|
||||
|
||||
|
||||
def test_deconcretize_root(test_env):
|
||||
with ev.read("test") as e:
|
||||
output = deconcretize("-y", "--root", "pkg-b@1.0")
|
||||
assert "No matching specs to deconcretize" in output
|
||||
assert len(e.concretized_order) == 2
|
||||
|
||||
deconcretize("-y", "--root", "pkg-a@2.0")
|
||||
specs = [s for s, _ in e.concretized_specs()]
|
||||
|
||||
assert len(specs) == 1
|
||||
assert specs[0].satisfies("pkg-a@1.0")
|
||||
|
||||
|
||||
def test_deconcretize_all_root(test_env):
|
||||
with ev.read("test") as e:
|
||||
with pytest.raises(SpackCommandError):
|
||||
deconcretize("-y", "--root", "pkg-a")
|
||||
|
||||
output = deconcretize("-y", "--root", "--all", "pkg-b")
|
||||
assert "No matching specs to deconcretize" in output
|
||||
assert len(e.concretized_order) == 2
|
||||
|
||||
deconcretize("-y", "--root", "--all", "pkg-a")
|
||||
specs = [s for s, _ in e.concretized_specs()]
|
||||
|
||||
assert len(specs) == 0
|
||||
|
||||
|
||||
def test_deconcretize_all(test_env):
|
||||
with ev.read("test") as e:
|
||||
with pytest.raises(SpackCommandError):
|
||||
deconcretize()
|
||||
deconcretize("-y", "--all")
|
||||
specs = [s for s, _ in e.concretized_specs()]
|
||||
|
||||
assert len(specs) == 0
|
@ -27,7 +27,9 @@
|
||||
import spack.package_base
|
||||
import spack.paths
|
||||
import spack.repo
|
||||
import spack.store
|
||||
import spack.util.spack_json as sjson
|
||||
import spack.util.spack_yaml
|
||||
from spack.cmd.env import _env_create
|
||||
from spack.main import SpackCommand, SpackCommandError
|
||||
from spack.spec import Spec
|
||||
@ -284,7 +286,7 @@ def setup_error(pkg, env):
|
||||
|
||||
_, err = capfd.readouterr()
|
||||
assert "cmake-client had issues!" in err
|
||||
assert "Warning: couldn't load runtime environment" in err
|
||||
assert "Warning: could not load runtime environment" in err
|
||||
|
||||
|
||||
def test_activate_adds_transitive_run_deps_to_path(install_mockery, mock_fetch, monkeypatch):
|
||||
@ -346,7 +348,7 @@ def test_env_install_two_specs_same_dep(install_mockery, mock_fetch, tmpdir, cap
|
||||
"""\
|
||||
spack:
|
||||
specs:
|
||||
- a
|
||||
- pkg-a
|
||||
- depb
|
||||
"""
|
||||
)
|
||||
@ -365,8 +367,8 @@ def test_env_install_two_specs_same_dep(install_mockery, mock_fetch, tmpdir, cap
|
||||
depb = spack.store.STORE.db.query_one("depb", installed=True)
|
||||
assert depb, "Expected depb to be installed"
|
||||
|
||||
a = spack.store.STORE.db.query_one("a", installed=True)
|
||||
assert a, "Expected a to be installed"
|
||||
a = spack.store.STORE.db.query_one("pkg-a", installed=True)
|
||||
assert a, "Expected pkg-a to be installed"
|
||||
|
||||
|
||||
def test_remove_after_concretize():
|
||||
@ -502,12 +504,12 @@ def test_env_activate_broken_view(
|
||||
# test that Spack detects the missing package and fails gracefully
|
||||
with spack.repo.use_repositories(mock_custom_repository):
|
||||
wrong_repo = env("activate", "--sh", "test")
|
||||
assert "Warning: couldn't load runtime environment" in wrong_repo
|
||||
assert "Warning: could not load runtime environment" in wrong_repo
|
||||
assert "Unknown namespace: builtin.mock" in wrong_repo
|
||||
|
||||
# test replacing repo fixes it
|
||||
normal_repo = env("activate", "--sh", "test")
|
||||
assert "Warning: couldn't load runtime environment" not in normal_repo
|
||||
assert "Warning: could not load runtime environment" not in normal_repo
|
||||
assert "Unknown namespace: builtin.mock" not in normal_repo
|
||||
|
||||
|
||||
@ -635,7 +637,7 @@ def test_env_view_external_prefix(tmp_path, mutable_database, mock_packages):
|
||||
"""\
|
||||
spack:
|
||||
specs:
|
||||
- a
|
||||
- pkg-a
|
||||
view: true
|
||||
"""
|
||||
)
|
||||
@ -643,9 +645,9 @@ def test_env_view_external_prefix(tmp_path, mutable_database, mock_packages):
|
||||
external_config = io.StringIO(
|
||||
"""\
|
||||
packages:
|
||||
a:
|
||||
pkg-a:
|
||||
externals:
|
||||
- spec: a@2.0
|
||||
- spec: pkg-a@2.0
|
||||
prefix: {a_prefix}
|
||||
buildable: false
|
||||
""".format(
|
||||
@ -3538,7 +3540,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"))
|
||||
|
@ -248,6 +248,7 @@ def _determine_variants(cls, exes, version_str):
|
||||
assert gcc.external_path == os.path.sep + os.path.join("opt", "gcc", "bin")
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("Fails spuriously on Windows")
|
||||
def test_new_entries_are_reported_correctly(mock_executable, mutable_config, monkeypatch):
|
||||
# Prepare an environment to detect a fake gcc
|
||||
gcc_exe = mock_executable("gcc", output="echo 4.2.1")
|
||||
|
@ -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)
|
||||
|
@ -25,7 +25,7 @@ def parser():
|
||||
def print_buffer(monkeypatch):
|
||||
buffer = []
|
||||
|
||||
def _print(*args):
|
||||
def _print(*args, **kwargs):
|
||||
buffer.extend(args)
|
||||
|
||||
monkeypatch.setattr(spack.cmd.info.color, "cprint", _print, raising=False)
|
||||
|
@ -88,7 +88,7 @@ def check(pkg):
|
||||
assert pkg.run_tests
|
||||
|
||||
monkeypatch.setattr(spack.package_base.PackageBase, "unit_test_check", check)
|
||||
install("--test=all", "a")
|
||||
install("--test=all", "pkg-a")
|
||||
|
||||
|
||||
def test_install_package_already_installed(
|
||||
@ -570,61 +570,58 @@ def test_cdash_upload_build_error(tmpdir, mock_fetch, install_mockery, capfd):
|
||||
@pytest.mark.disable_clean_stage_check
|
||||
def test_cdash_upload_clean_build(tmpdir, mock_fetch, install_mockery, capfd):
|
||||
# capfd interferes with Spack's capturing of e.g., Build.xml output
|
||||
with capfd.disabled():
|
||||
with tmpdir.as_cwd():
|
||||
install("--log-file=cdash_reports", "--log-format=cdash", "a")
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("a_Build.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
assert "</Build>" in content
|
||||
assert "<Text>" not in content
|
||||
with capfd.disabled(), tmpdir.as_cwd():
|
||||
install("--log-file=cdash_reports", "--log-format=cdash", "pkg-a")
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("pkg-a_Build.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
assert "</Build>" in content
|
||||
assert "<Text>" not in content
|
||||
|
||||
|
||||
@pytest.mark.disable_clean_stage_check
|
||||
def test_cdash_upload_extra_params(tmpdir, mock_fetch, install_mockery, capfd):
|
||||
# capfd interferes with Spack's capture of e.g., Build.xml output
|
||||
with capfd.disabled():
|
||||
with tmpdir.as_cwd():
|
||||
install(
|
||||
"--log-file=cdash_reports",
|
||||
"--log-format=cdash",
|
||||
"--cdash-build=my_custom_build",
|
||||
"--cdash-site=my_custom_site",
|
||||
"--cdash-track=my_custom_track",
|
||||
"a",
|
||||
)
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("a_Build.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
assert 'Site BuildName="my_custom_build - a"' in content
|
||||
assert 'Name="my_custom_site"' in content
|
||||
assert "-my_custom_track" in content
|
||||
with capfd.disabled(), tmpdir.as_cwd():
|
||||
install(
|
||||
"--log-file=cdash_reports",
|
||||
"--log-format=cdash",
|
||||
"--cdash-build=my_custom_build",
|
||||
"--cdash-site=my_custom_site",
|
||||
"--cdash-track=my_custom_track",
|
||||
"pkg-a",
|
||||
)
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("pkg-a_Build.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
assert 'Site BuildName="my_custom_build - pkg-a"' in content
|
||||
assert 'Name="my_custom_site"' in content
|
||||
assert "-my_custom_track" in content
|
||||
|
||||
|
||||
@pytest.mark.disable_clean_stage_check
|
||||
def test_cdash_buildstamp_param(tmpdir, mock_fetch, install_mockery, capfd):
|
||||
# capfd interferes with Spack's capture of e.g., Build.xml output
|
||||
with capfd.disabled():
|
||||
with tmpdir.as_cwd():
|
||||
cdash_track = "some_mocked_track"
|
||||
buildstamp_format = "%Y%m%d-%H%M-{0}".format(cdash_track)
|
||||
buildstamp = time.strftime(buildstamp_format, time.localtime(int(time.time())))
|
||||
install(
|
||||
"--log-file=cdash_reports",
|
||||
"--log-format=cdash",
|
||||
"--cdash-buildstamp={0}".format(buildstamp),
|
||||
"a",
|
||||
)
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("a_Build.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
assert buildstamp in content
|
||||
with capfd.disabled(), tmpdir.as_cwd():
|
||||
cdash_track = "some_mocked_track"
|
||||
buildstamp_format = "%Y%m%d-%H%M-{0}".format(cdash_track)
|
||||
buildstamp = time.strftime(buildstamp_format, time.localtime(int(time.time())))
|
||||
install(
|
||||
"--log-file=cdash_reports",
|
||||
"--log-format=cdash",
|
||||
"--cdash-buildstamp={0}".format(buildstamp),
|
||||
"pkg-a",
|
||||
)
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("pkg-a_Build.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
assert buildstamp in content
|
||||
|
||||
|
||||
@pytest.mark.disable_clean_stage_check
|
||||
@ -632,38 +629,37 @@ def test_cdash_install_from_spec_json(
|
||||
tmpdir, mock_fetch, install_mockery, capfd, mock_packages, mock_archive, config
|
||||
):
|
||||
# capfd interferes with Spack's capturing
|
||||
with capfd.disabled():
|
||||
with tmpdir.as_cwd():
|
||||
spec_json_path = str(tmpdir.join("spec.json"))
|
||||
with capfd.disabled(), tmpdir.as_cwd():
|
||||
spec_json_path = str(tmpdir.join("spec.json"))
|
||||
|
||||
pkg_spec = Spec("a")
|
||||
pkg_spec.concretize()
|
||||
pkg_spec = Spec("pkg-a")
|
||||
pkg_spec.concretize()
|
||||
|
||||
with open(spec_json_path, "w") as fd:
|
||||
fd.write(pkg_spec.to_json(hash=ht.dag_hash))
|
||||
with open(spec_json_path, "w") as fd:
|
||||
fd.write(pkg_spec.to_json(hash=ht.dag_hash))
|
||||
|
||||
install(
|
||||
"--log-format=cdash",
|
||||
"--log-file=cdash_reports",
|
||||
"--cdash-build=my_custom_build",
|
||||
"--cdash-site=my_custom_site",
|
||||
"--cdash-track=my_custom_track",
|
||||
"-f",
|
||||
spec_json_path,
|
||||
)
|
||||
install(
|
||||
"--log-format=cdash",
|
||||
"--log-file=cdash_reports",
|
||||
"--cdash-build=my_custom_build",
|
||||
"--cdash-site=my_custom_site",
|
||||
"--cdash-track=my_custom_track",
|
||||
"-f",
|
||||
spec_json_path,
|
||||
)
|
||||
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("a_Configure.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
install_command_regex = re.compile(
|
||||
r"<ConfigureCommand>(.+)</ConfigureCommand>", re.MULTILINE | re.DOTALL
|
||||
)
|
||||
m = install_command_regex.search(content)
|
||||
assert m
|
||||
install_command = m.group(1)
|
||||
assert "a@" in install_command
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("pkg-a_Configure.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
install_command_regex = re.compile(
|
||||
r"<ConfigureCommand>(.+)</ConfigureCommand>", re.MULTILINE | re.DOTALL
|
||||
)
|
||||
m = install_command_regex.search(content)
|
||||
assert m
|
||||
install_command = m.group(1)
|
||||
assert "pkg-a@" in install_command
|
||||
|
||||
|
||||
@pytest.mark.disable_clean_stage_check
|
||||
@ -795,15 +791,15 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock
|
||||
# ^libdwarf
|
||||
# ^mpich
|
||||
# libelf@0.8.10
|
||||
# a~bvv
|
||||
# ^b
|
||||
# a
|
||||
# ^b
|
||||
# pkg-a~bvv
|
||||
# ^pkg-b
|
||||
# pkg-a
|
||||
# ^pkg-b
|
||||
e = ev.create("test", with_view=False)
|
||||
e.add("mpileaks")
|
||||
e.add("libelf@0.8.10") # so env has both root and dep libelf specs
|
||||
e.add("a")
|
||||
e.add("a ~bvv")
|
||||
e.add("pkg-a")
|
||||
e.add("pkg-a ~bvv")
|
||||
e.concretize()
|
||||
e.write()
|
||||
env_specs = e.all_specs()
|
||||
@ -814,9 +810,9 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock
|
||||
|
||||
# First find and remember some target concrete specs in the environment
|
||||
for e_spec in env_specs:
|
||||
if e_spec.satisfies(Spec("a ~bvv")):
|
||||
if e_spec.satisfies(Spec("pkg-a ~bvv")):
|
||||
a_spec = e_spec
|
||||
elif e_spec.name == "b":
|
||||
elif e_spec.name == "pkg-b":
|
||||
b_spec = e_spec
|
||||
elif e_spec.satisfies(Spec("mpi")):
|
||||
mpi_spec = e_spec
|
||||
@ -839,8 +835,8 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock
|
||||
assert "You can add specs to the environment with 'spack add " in inst_out
|
||||
|
||||
# Without --add, ensure that two packages "a" get installed
|
||||
inst_out = install("a", output=str)
|
||||
assert len([x for x in e.all_specs() if x.installed and x.name == "a"]) == 2
|
||||
inst_out = install("pkg-a", output=str)
|
||||
assert len([x for x in e.all_specs() if x.installed and x.name == "pkg-a"]) == 2
|
||||
|
||||
# Install an unambiguous dependency spec (that already exists as a dep
|
||||
# in the environment) and make sure it gets installed (w/ deps),
|
||||
@ -873,7 +869,7 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock
|
||||
# root of the environment as well as installed.
|
||||
assert b_spec not in e.roots()
|
||||
|
||||
install("--add", "b")
|
||||
install("--add", "pkg-b")
|
||||
|
||||
assert b_spec in e.roots()
|
||||
assert b_spec not in e.uninstalled_specs()
|
||||
@ -904,39 +900,37 @@ 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", "pkg-a")
|
||||
assert "Using CDash auth token from environment" in out
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("Windows log_output logs phase header out of order")
|
||||
@pytest.mark.disable_clean_stage_check
|
||||
def test_cdash_configure_warning(tmpdir, mock_fetch, install_mockery, capfd):
|
||||
# capfd interferes with Spack's capturing of e.g., Build.xml output
|
||||
with capfd.disabled():
|
||||
with tmpdir.as_cwd():
|
||||
# Test would fail if install raised an error.
|
||||
with capfd.disabled(), tmpdir.as_cwd():
|
||||
# Test would fail if install raised an error.
|
||||
|
||||
# Ensure that even on non-x86_64 architectures, there are no
|
||||
# dependencies installed
|
||||
spec = spack.spec.Spec("configure-warning").concretized()
|
||||
spec.clear_dependencies()
|
||||
specfile = "./spec.json"
|
||||
with open(specfile, "w") as f:
|
||||
f.write(spec.to_json())
|
||||
# Ensure that even on non-x86_64 architectures, there are no
|
||||
# dependencies installed
|
||||
spec = Spec("configure-warning").concretized()
|
||||
spec.clear_dependencies()
|
||||
specfile = "./spec.json"
|
||||
with open(specfile, "w") as f:
|
||||
f.write(spec.to_json())
|
||||
|
||||
install("--log-file=cdash_reports", "--log-format=cdash", specfile)
|
||||
# Verify Configure.xml exists with expected contents.
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("Configure.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
assert "foo: No such file or directory" in content
|
||||
install("--log-file=cdash_reports", "--log-format=cdash", specfile)
|
||||
# Verify Configure.xml exists with expected contents.
|
||||
report_dir = tmpdir.join("cdash_reports")
|
||||
assert report_dir in tmpdir.listdir()
|
||||
report_file = report_dir.join("Configure.xml")
|
||||
assert report_file in report_dir.listdir()
|
||||
content = report_file.open().read()
|
||||
assert "foo: No such file or directory" in content
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("ArchSpec gives test platform debian rather than windows")
|
||||
@ -953,7 +947,7 @@ def test_compiler_bootstrap(
|
||||
assert CompilerSpec("gcc@=12.0") not in compilers.all_compiler_specs()
|
||||
|
||||
# Test succeeds if it does not raise an error
|
||||
install("a%gcc@=12.0")
|
||||
install("pkg-a%gcc@=12.0")
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("Binary mirrors not supported on windows")
|
||||
@ -993,8 +987,8 @@ def test_compiler_bootstrap_from_binary_mirror(
|
||||
# Now make sure that when the compiler is installed from binary mirror,
|
||||
# it also gets configured as a compiler. Test succeeds if it does not
|
||||
# raise an error
|
||||
install("--no-check-signature", "--cache-only", "--only", "dependencies", "b%gcc@=10.2.0")
|
||||
install("--no-cache", "--only", "package", "b%gcc@10.2.0")
|
||||
install("--no-check-signature", "--cache-only", "--only", "dependencies", "pkg-b%gcc@=10.2.0")
|
||||
install("--no-cache", "--only", "package", "pkg-b%gcc@10.2.0")
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("ArchSpec gives test platform debian rather than windows")
|
||||
@ -1014,7 +1008,7 @@ def test_compiler_bootstrap_already_installed(
|
||||
|
||||
# Test succeeds if it does not raise an error
|
||||
install("gcc@=12.0")
|
||||
install("a%gcc@=12.0")
|
||||
install("pkg-a%gcc@=12.0")
|
||||
|
||||
|
||||
def test_install_fails_no_args(tmpdir):
|
||||
@ -1196,7 +1190,7 @@ def test_report_filename_for_cdash(install_mockery_mutable_config, mock_fetch):
|
||||
parser = argparse.ArgumentParser()
|
||||
spack.cmd.install.setup_parser(parser)
|
||||
args = parser.parse_args(
|
||||
["--cdash-upload-url", "https://blahblah/submit.php?project=debugging", "a"]
|
||||
["--cdash-upload-url", "https://blahblah/submit.php?project=debugging", "pkg-a"]
|
||||
)
|
||||
specs = spack.cmd.install.concrete_specs_from_cli(args, {})
|
||||
filename = spack.cmd.install.report_filename(args, specs)
|
||||
|
@ -133,7 +133,7 @@ def test_maintainers_list_packages(mock_packages, capfd):
|
||||
|
||||
|
||||
def test_maintainers_list_fails(mock_packages, capfd):
|
||||
out = maintainers("a", fail_on_error=False)
|
||||
out = maintainers("pkg-a", fail_on_error=False)
|
||||
assert not out
|
||||
assert maintainers.returncode == 1
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
import spack.config
|
||||
import spack.main
|
||||
import spack.modules
|
||||
import spack.spec
|
||||
import spack.store
|
||||
|
||||
module = spack.main.SpackCommand("module")
|
||||
@ -178,8 +179,8 @@ def test_setdefault_command(mutable_database, mutable_config):
|
||||
}
|
||||
}
|
||||
spack.config.set("modules", data)
|
||||
# Install two different versions of a package
|
||||
other_spec, preferred = "a@1.0", "a@2.0"
|
||||
# Install two different versions of pkg-a
|
||||
other_spec, preferred = "pkg-a@1.0", "pkg-a@2.0"
|
||||
|
||||
spack.spec.Spec(other_spec).concretized().package.do_install(fake=True)
|
||||
spack.spec.Spec(preferred).concretized().package.do_install(fake=True)
|
||||
|
@ -28,8 +28,8 @@ def install(self, spec, prefix):
|
||||
pass
|
||||
"""
|
||||
|
||||
abc = set(("pkg-a", "pkg-b", "pkg-c"))
|
||||
abd = set(("pkg-a", "pkg-b", "pkg-d"))
|
||||
abc = {"mockpkg-a", "mockpkg-b", "mockpkg-c"}
|
||||
abd = {"mockpkg-a", "mockpkg-b", "mockpkg-d"}
|
||||
|
||||
|
||||
# Force all tests to use a git repository *in* the mock packages repo.
|
||||
@ -53,27 +53,33 @@ def mock_pkg_git_repo(git, tmpdir_factory):
|
||||
git("config", "user.name", "Spack Testing")
|
||||
git("-c", "commit.gpgsign=false", "commit", "-m", "initial mock repo commit")
|
||||
|
||||
# add commit with pkg-a, pkg-b, pkg-c packages
|
||||
mkdirp("pkg-a", "pkg-b", "pkg-c")
|
||||
with open("pkg-a/package.py", "w") as f:
|
||||
# add commit with mockpkg-a, mockpkg-b, mockpkg-c packages
|
||||
mkdirp("mockpkg-a", "mockpkg-b", "mockpkg-c")
|
||||
with open("mockpkg-a/package.py", "w") as f:
|
||||
f.write(pkg_template.format(name="PkgA"))
|
||||
with open("pkg-b/package.py", "w") as f:
|
||||
with open("mockpkg-b/package.py", "w") as f:
|
||||
f.write(pkg_template.format(name="PkgB"))
|
||||
with open("pkg-c/package.py", "w") as f:
|
||||
with open("mockpkg-c/package.py", "w") as f:
|
||||
f.write(pkg_template.format(name="PkgC"))
|
||||
git("add", "pkg-a", "pkg-b", "pkg-c")
|
||||
git("-c", "commit.gpgsign=false", "commit", "-m", "add pkg-a, pkg-b, pkg-c")
|
||||
git("add", "mockpkg-a", "mockpkg-b", "mockpkg-c")
|
||||
git("-c", "commit.gpgsign=false", "commit", "-m", "add mockpkg-a, mockpkg-b, mockpkg-c")
|
||||
|
||||
# remove pkg-c, add pkg-d
|
||||
with open("pkg-b/package.py", "a") as f:
|
||||
f.write("\n# change pkg-b")
|
||||
git("add", "pkg-b")
|
||||
mkdirp("pkg-d")
|
||||
with open("pkg-d/package.py", "w") as f:
|
||||
# remove mockpkg-c, add mockpkg-d
|
||||
with open("mockpkg-b/package.py", "a") as f:
|
||||
f.write("\n# change mockpkg-b")
|
||||
git("add", "mockpkg-b")
|
||||
mkdirp("mockpkg-d")
|
||||
with open("mockpkg-d/package.py", "w") as f:
|
||||
f.write(pkg_template.format(name="PkgD"))
|
||||
git("add", "pkg-d")
|
||||
git("rm", "-rf", "pkg-c")
|
||||
git("-c", "commit.gpgsign=false", "commit", "-m", "change pkg-b, remove pkg-c, add pkg-d")
|
||||
git("add", "mockpkg-d")
|
||||
git("rm", "-rf", "mockpkg-c")
|
||||
git(
|
||||
"-c",
|
||||
"commit.gpgsign=false",
|
||||
"commit",
|
||||
"-m",
|
||||
"change mockpkg-b, remove mockpkg-c, add mockpkg-d",
|
||||
)
|
||||
|
||||
with spack.repo.use_repositories(str(repo_path)):
|
||||
yield mock_repo_packages
|
||||
@ -86,12 +92,11 @@ def mock_pkg_names():
|
||||
# Be sure to include virtual packages since packages with stand-alone
|
||||
# tests may inherit additional tests from the virtuals they provide,
|
||||
# such as packages that implement `mpi`.
|
||||
names = set(
|
||||
return {
|
||||
name
|
||||
for name in repo.all_package_names(include_virtuals=True)
|
||||
if not name.startswith("pkg-")
|
||||
)
|
||||
return names
|
||||
if not name.startswith("mockpkg-")
|
||||
}
|
||||
|
||||
|
||||
def split(output):
|
||||
@ -113,17 +118,17 @@ def test_mock_packages_path(mock_packages):
|
||||
|
||||
def test_pkg_add(git, mock_pkg_git_repo):
|
||||
with working_dir(mock_pkg_git_repo):
|
||||
mkdirp("pkg-e")
|
||||
with open("pkg-e/package.py", "w") as f:
|
||||
mkdirp("mockpkg-e")
|
||||
with open("mockpkg-e/package.py", "w") as f:
|
||||
f.write(pkg_template.format(name="PkgE"))
|
||||
|
||||
pkg("add", "pkg-e")
|
||||
pkg("add", "mockpkg-e")
|
||||
|
||||
with working_dir(mock_pkg_git_repo):
|
||||
try:
|
||||
assert "A pkg-e/package.py" in git("status", "--short", output=str)
|
||||
assert "A mockpkg-e/package.py" in git("status", "--short", output=str)
|
||||
finally:
|
||||
shutil.rmtree("pkg-e")
|
||||
shutil.rmtree("mockpkg-e")
|
||||
# Removing a package mid-run disrupts Spack's caching
|
||||
if spack.repo.PATH.repos[0]._fast_package_checker:
|
||||
spack.repo.PATH.repos[0]._fast_package_checker.invalidate()
|
||||
@ -138,10 +143,10 @@ def test_pkg_list(mock_pkg_git_repo, mock_pkg_names):
|
||||
assert sorted(mock_pkg_names) == sorted(out)
|
||||
|
||||
out = split(pkg("list", "HEAD^"))
|
||||
assert sorted(mock_pkg_names.union(["pkg-a", "pkg-b", "pkg-c"])) == sorted(out)
|
||||
assert sorted(mock_pkg_names.union(["mockpkg-a", "mockpkg-b", "mockpkg-c"])) == sorted(out)
|
||||
|
||||
out = split(pkg("list", "HEAD"))
|
||||
assert sorted(mock_pkg_names.union(["pkg-a", "pkg-b", "pkg-d"])) == sorted(out)
|
||||
assert sorted(mock_pkg_names.union(["mockpkg-a", "mockpkg-b", "mockpkg-d"])) == sorted(out)
|
||||
|
||||
# test with three dots to make sure pkg calls `git merge-base`
|
||||
out = split(pkg("list", "HEAD^^..."))
|
||||
@ -151,25 +156,25 @@ def test_pkg_list(mock_pkg_git_repo, mock_pkg_names):
|
||||
@pytest.mark.not_on_windows("stdout format conflict")
|
||||
def test_pkg_diff(mock_pkg_git_repo, mock_pkg_names):
|
||||
out = split(pkg("diff", "HEAD^^", "HEAD^"))
|
||||
assert out == ["HEAD^:", "pkg-a", "pkg-b", "pkg-c"]
|
||||
assert out == ["HEAD^:", "mockpkg-a", "mockpkg-b", "mockpkg-c"]
|
||||
|
||||
out = split(pkg("diff", "HEAD^^", "HEAD"))
|
||||
assert out == ["HEAD:", "pkg-a", "pkg-b", "pkg-d"]
|
||||
assert out == ["HEAD:", "mockpkg-a", "mockpkg-b", "mockpkg-d"]
|
||||
|
||||
out = split(pkg("diff", "HEAD^", "HEAD"))
|
||||
assert out == ["HEAD^:", "pkg-c", "HEAD:", "pkg-d"]
|
||||
assert out == ["HEAD^:", "mockpkg-c", "HEAD:", "mockpkg-d"]
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("stdout format conflict")
|
||||
def test_pkg_added(mock_pkg_git_repo):
|
||||
out = split(pkg("added", "HEAD^^", "HEAD^"))
|
||||
assert ["pkg-a", "pkg-b", "pkg-c"] == out
|
||||
assert ["mockpkg-a", "mockpkg-b", "mockpkg-c"] == out
|
||||
|
||||
out = split(pkg("added", "HEAD^^", "HEAD"))
|
||||
assert ["pkg-a", "pkg-b", "pkg-d"] == out
|
||||
assert ["mockpkg-a", "mockpkg-b", "mockpkg-d"] == out
|
||||
|
||||
out = split(pkg("added", "HEAD^", "HEAD"))
|
||||
assert ["pkg-d"] == out
|
||||
assert ["mockpkg-d"] == out
|
||||
|
||||
out = split(pkg("added", "HEAD", "HEAD"))
|
||||
assert out == []
|
||||
@ -184,7 +189,7 @@ def test_pkg_removed(mock_pkg_git_repo):
|
||||
assert out == []
|
||||
|
||||
out = split(pkg("removed", "HEAD^", "HEAD"))
|
||||
assert out == ["pkg-c"]
|
||||
assert out == ["mockpkg-c"]
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("stdout format conflict")
|
||||
@ -196,34 +201,34 @@ def test_pkg_changed(mock_pkg_git_repo):
|
||||
assert out == []
|
||||
|
||||
out = split(pkg("changed", "--type", "a", "HEAD^^", "HEAD^"))
|
||||
assert out == ["pkg-a", "pkg-b", "pkg-c"]
|
||||
assert out == ["mockpkg-a", "mockpkg-b", "mockpkg-c"]
|
||||
|
||||
out = split(pkg("changed", "--type", "r", "HEAD^^", "HEAD^"))
|
||||
assert out == []
|
||||
|
||||
out = split(pkg("changed", "--type", "ar", "HEAD^^", "HEAD^"))
|
||||
assert out == ["pkg-a", "pkg-b", "pkg-c"]
|
||||
assert out == ["mockpkg-a", "mockpkg-b", "mockpkg-c"]
|
||||
|
||||
out = split(pkg("changed", "--type", "arc", "HEAD^^", "HEAD^"))
|
||||
assert out == ["pkg-a", "pkg-b", "pkg-c"]
|
||||
assert out == ["mockpkg-a", "mockpkg-b", "mockpkg-c"]
|
||||
|
||||
out = split(pkg("changed", "HEAD^", "HEAD"))
|
||||
assert out == ["pkg-b"]
|
||||
assert out == ["mockpkg-b"]
|
||||
|
||||
out = split(pkg("changed", "--type", "c", "HEAD^", "HEAD"))
|
||||
assert out == ["pkg-b"]
|
||||
assert out == ["mockpkg-b"]
|
||||
|
||||
out = split(pkg("changed", "--type", "a", "HEAD^", "HEAD"))
|
||||
assert out == ["pkg-d"]
|
||||
assert out == ["mockpkg-d"]
|
||||
|
||||
out = split(pkg("changed", "--type", "r", "HEAD^", "HEAD"))
|
||||
assert out == ["pkg-c"]
|
||||
assert out == ["mockpkg-c"]
|
||||
|
||||
out = split(pkg("changed", "--type", "ar", "HEAD^", "HEAD"))
|
||||
assert out == ["pkg-c", "pkg-d"]
|
||||
assert out == ["mockpkg-c", "mockpkg-d"]
|
||||
|
||||
out = split(pkg("changed", "--type", "arc", "HEAD^", "HEAD"))
|
||||
assert out == ["pkg-b", "pkg-c", "pkg-d"]
|
||||
assert out == ["mockpkg-b", "mockpkg-c", "mockpkg-d"]
|
||||
|
||||
# invalid type argument
|
||||
with pytest.raises(spack.main.SpackCommandError):
|
||||
@ -289,7 +294,7 @@ def test_pkg_canonical_source(mock_packages):
|
||||
|
||||
|
||||
def test_pkg_hash(mock_packages):
|
||||
output = pkg("hash", "a", "b").strip().split()
|
||||
output = pkg("hash", "pkg-a", "pkg-b").strip().split()
|
||||
assert len(output) == 2 and all(len(elt) == 32 for elt in output)
|
||||
|
||||
output = pkg("hash", "multimethod").strip().split()
|
||||
|
@ -59,7 +59,7 @@ def test_spec_concretizer_args(mutable_config, mutable_database):
|
||||
def test_spec_parse_dependency_variant_value():
|
||||
"""Verify that we can provide multiple key=value variants to multiple separate
|
||||
packages within a spec string."""
|
||||
output = spec("multivalue-variant fee=barbaz ^ a foobar=baz")
|
||||
output = spec("multivalue-variant fee=barbaz ^ pkg-a foobar=baz")
|
||||
|
||||
assert "fee=barbaz" in output
|
||||
assert "foobar=baz" in output
|
||||
|
@ -10,10 +10,14 @@
|
||||
|
||||
from llnl.util.filesystem import copy_tree
|
||||
|
||||
import spack.cmd.common.arguments
|
||||
import spack.cmd.install
|
||||
import spack.cmd.test
|
||||
import spack.config
|
||||
import spack.install_test
|
||||
import spack.package_base
|
||||
import spack.paths
|
||||
import spack.spec
|
||||
import spack.store
|
||||
from spack.install_test import TestStatus
|
||||
from spack.main import SpackCommand
|
||||
|
@ -11,7 +11,7 @@
|
||||
import llnl.util.filesystem as fs
|
||||
|
||||
import spack.compiler
|
||||
import spack.compilers as compilers
|
||||
import spack.compilers
|
||||
import spack.spec
|
||||
import spack.util.environment
|
||||
from spack.compiler import Compiler
|
||||
@ -25,12 +25,14 @@ class MockOs:
|
||||
pass
|
||||
|
||||
compiler_name = "gcc"
|
||||
compiler_cls = compilers.class_for_compiler_name(compiler_name)
|
||||
compiler_cls = spack.compilers.class_for_compiler_name(compiler_name)
|
||||
monkeypatch.setattr(compiler_cls, "cc_version", lambda x: version)
|
||||
|
||||
compiler_id = compilers.CompilerID(os=MockOs, compiler_name=compiler_name, version=None)
|
||||
variation = compilers.NameVariation(prefix="", suffix="")
|
||||
return compilers.DetectVersionArgs(
|
||||
compiler_id = spack.compilers.CompilerID(
|
||||
os=MockOs, compiler_name=compiler_name, version=None
|
||||
)
|
||||
variation = spack.compilers.NameVariation(prefix="", suffix="")
|
||||
return spack.compilers.DetectVersionArgs(
|
||||
id=compiler_id, variation=variation, language="cc", path=path
|
||||
)
|
||||
|
||||
@ -56,15 +58,21 @@ def test_multiple_conflicting_compiler_definitions(mutable_config):
|
||||
mutable_config.update_config("compilers", compiler_config)
|
||||
|
||||
arch_spec = spack.spec.ArchSpec(("test", "test", "test"))
|
||||
cmp = compilers.compiler_for_spec("clang@=0.0.0", arch_spec)
|
||||
cmp = spack.compilers.compiler_for_spec("clang@=0.0.0", arch_spec)
|
||||
assert cmp.f77 == "f77"
|
||||
|
||||
|
||||
def test_get_compiler_duplicates(config):
|
||||
def test_get_compiler_duplicates(mutable_config, compiler_factory):
|
||||
# In this case there is only one instance of the specified compiler in
|
||||
# the test configuration (so it is not actually a duplicate), but the
|
||||
# method behaves the same.
|
||||
cfg_file_to_duplicates = compilers.get_compiler_duplicates(
|
||||
cnl_compiler = compiler_factory(spec="gcc@4.5.0", operating_system="CNL")
|
||||
# CNL compiler has no target attribute, and this is essential to make detection pass
|
||||
del cnl_compiler["compiler"]["target"]
|
||||
mutable_config.set(
|
||||
"compilers", [compiler_factory(spec="gcc@4.5.0", operating_system="SuSE11"), cnl_compiler]
|
||||
)
|
||||
cfg_file_to_duplicates = spack.compilers.get_compiler_duplicates(
|
||||
"gcc@4.5.0", spack.spec.ArchSpec("cray-CNL-xeon")
|
||||
)
|
||||
|
||||
@ -73,13 +81,6 @@ def test_get_compiler_duplicates(config):
|
||||
assert len(duplicates) == 1
|
||||
|
||||
|
||||
def test_all_compilers(config):
|
||||
all_compilers = compilers.all_compilers()
|
||||
filtered = [x for x in all_compilers if str(x.spec) == "clang@=3.3"]
|
||||
filtered = [x for x in filtered if x.operating_system == "SuSE11"]
|
||||
assert len(filtered) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_version,expected_version,expected_error",
|
||||
[(None, None, "Couldn't get version for compiler /usr/bin/gcc"), ("4.9", "4.9", None)],
|
||||
@ -88,7 +89,7 @@ def test_version_detection_is_empty(
|
||||
make_args_for_version, input_version, expected_version, expected_error
|
||||
):
|
||||
args = make_args_for_version(version=input_version)
|
||||
result, error = compilers.detect_version(args)
|
||||
result, error = spack.compilers.detect_version(args)
|
||||
if not error:
|
||||
assert result.id.version == expected_version
|
||||
|
||||
@ -104,7 +105,7 @@ def test_compiler_flags_from_config_are_grouped():
|
||||
"modules": None,
|
||||
}
|
||||
|
||||
compiler = compilers.compiler_from_dict(compiler_entry)
|
||||
compiler = spack.compilers.compiler_from_dict(compiler_entry)
|
||||
assert any(x == "-foo-flag foo-val" for x in compiler.flags["cflags"])
|
||||
|
||||
|
||||
@ -253,8 +254,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
|
||||
+ """'
|
||||
@ -288,7 +289,7 @@ def flag_value(flag, spec):
|
||||
else:
|
||||
compiler_entry = copy(default_compiler_entry)
|
||||
compiler_entry["spec"] = spec
|
||||
compiler = compilers.compiler_from_dict(compiler_entry)
|
||||
compiler = spack.compilers.compiler_from_dict(compiler_entry)
|
||||
|
||||
return getattr(compiler, flag)
|
||||
|
||||
@ -658,9 +659,27 @@ def test_xl_r_flags():
|
||||
"compiler_spec,expected_result",
|
||||
[("gcc@4.7.2", False), ("clang@3.3", False), ("clang@8.0.0", True)],
|
||||
)
|
||||
def test_detecting_mixed_toolchains(compiler_spec, expected_result, config):
|
||||
compiler = compilers.compilers_for_spec(compiler_spec).pop()
|
||||
assert compilers.is_mixed_toolchain(compiler) is expected_result
|
||||
def test_detecting_mixed_toolchains(
|
||||
compiler_spec, expected_result, mutable_config, compiler_factory
|
||||
):
|
||||
mixed_c = compiler_factory(spec="clang@8.0.0", operating_system="debian6")
|
||||
mixed_c["compiler"]["paths"] = {
|
||||
"cc": "/path/to/clang-8",
|
||||
"cxx": "/path/to/clang++-8",
|
||||
"f77": "/path/to/gfortran-9",
|
||||
"fc": "/path/to/gfortran-9",
|
||||
}
|
||||
mutable_config.set(
|
||||
"compilers",
|
||||
[
|
||||
compiler_factory(spec="gcc@4.7.2", operating_system="debian6"),
|
||||
compiler_factory(spec="clang@3.3", operating_system="debian6"),
|
||||
mixed_c,
|
||||
],
|
||||
)
|
||||
|
||||
compiler = spack.compilers.compilers_for_spec(compiler_spec).pop()
|
||||
assert spack.compilers.is_mixed_toolchain(compiler) is expected_result
|
||||
|
||||
|
||||
@pytest.mark.regression("14798,13733")
|
||||
@ -701,8 +720,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
|
||||
"""
|
||||
@ -739,6 +758,49 @@ def module(*args):
|
||||
assert version == test_version
|
||||
|
||||
|
||||
@pytest.mark.regression("42679")
|
||||
def test_get_compilers(config):
|
||||
"""Tests that we can select compilers whose versions differ only for a suffix."""
|
||||
common = {
|
||||
"flags": {},
|
||||
"operating_system": "ubuntu23.10",
|
||||
"target": "x86_64",
|
||||
"modules": [],
|
||||
"environment": {},
|
||||
"extra_rpaths": [],
|
||||
}
|
||||
with_suffix = {
|
||||
"spec": "gcc@13.2.0-suffix",
|
||||
"paths": {
|
||||
"cc": "/usr/bin/gcc-13.2.0-suffix",
|
||||
"cxx": "/usr/bin/g++-13.2.0-suffix",
|
||||
"f77": "/usr/bin/gfortran-13.2.0-suffix",
|
||||
"fc": "/usr/bin/gfortran-13.2.0-suffix",
|
||||
},
|
||||
**common,
|
||||
}
|
||||
without_suffix = {
|
||||
"spec": "gcc@13.2.0",
|
||||
"paths": {
|
||||
"cc": "/usr/bin/gcc-13.2.0",
|
||||
"cxx": "/usr/bin/g++-13.2.0",
|
||||
"f77": "/usr/bin/gfortran-13.2.0",
|
||||
"fc": "/usr/bin/gfortran-13.2.0",
|
||||
},
|
||||
**common,
|
||||
}
|
||||
|
||||
compilers = [{"compiler": without_suffix}, {"compiler": with_suffix}]
|
||||
|
||||
assert spack.compilers.get_compilers(
|
||||
compilers, cspec=spack.spec.CompilerSpec("gcc@=13.2.0-suffix")
|
||||
) == [spack.compilers._compiler_from_config_entry(with_suffix)]
|
||||
|
||||
assert spack.compilers.get_compilers(
|
||||
compilers, cspec=spack.spec.CompilerSpec("gcc@=13.2.0")
|
||||
) == [spack.compilers._compiler_from_config_entry(without_suffix)]
|
||||
|
||||
|
||||
def test_compiler_get_real_version_fails(working_env, monkeypatch, tmpdir):
|
||||
# Test variables
|
||||
test_version = "2.2.2"
|
||||
@ -747,8 +809,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 +863,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
|
||||
|
@ -23,6 +23,8 @@
|
||||
import spack.platforms
|
||||
import spack.repo
|
||||
import spack.solver.asp
|
||||
import spack.store
|
||||
import spack.util.file_cache
|
||||
import spack.variant as vt
|
||||
from spack.concretize import find_spec
|
||||
from spack.spec import CompilerSpec, Spec
|
||||
@ -203,7 +205,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
|
||||
@ -222,6 +226,20 @@ def change(self, changes=None):
|
||||
yield _changing_pkg
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def clang12_with_flags(compiler_factory):
|
||||
c = compiler_factory(spec="clang@12.2.0", operating_system="redhat6")
|
||||
c["compiler"]["flags"] = {"cflags": "-O3", "cxxflags": "-O3"}
|
||||
return c
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def gcc11_with_flags(compiler_factory):
|
||||
c = compiler_factory(spec="gcc@11.1.0", operating_system="redhat6")
|
||||
c["compiler"]["flags"] = {"cflags": "-O0 -g", "cxxflags": "-O0 -g", "fflags": "-O0 -g"}
|
||||
return c
|
||||
|
||||
|
||||
# This must use the mutable_config fixture because the test
|
||||
# adjusting_default_target_based_on_compiler uses the current_host fixture,
|
||||
# which changes the config.
|
||||
@ -314,18 +332,35 @@ def test_provides_handles_multiple_providers_of_same_version(self):
|
||||
assert Spec("builtin.mock.multi-provider-mpi@1.10.0") in providers
|
||||
assert Spec("builtin.mock.multi-provider-mpi@1.8.8") in providers
|
||||
|
||||
def test_different_compilers_get_different_flags(self):
|
||||
def test_different_compilers_get_different_flags(
|
||||
self, mutable_config, clang12_with_flags, gcc11_with_flags
|
||||
):
|
||||
"""Tests that nodes get the flags of the associated compiler."""
|
||||
mutable_config.set("compilers", [clang12_with_flags, gcc11_with_flags])
|
||||
client = Spec(
|
||||
"cmake-client %gcc@11.1.0 platform=test os=fe target=fe"
|
||||
+ " ^cmake %clang@12.2.0 platform=test os=fe target=fe"
|
||||
)
|
||||
client.concretize()
|
||||
" ^cmake %clang@12.2.0 platform=test os=fe target=fe"
|
||||
).concretized()
|
||||
cmake = client["cmake"]
|
||||
assert set(client.compiler_flags["cflags"]) == set(["-O0", "-g"])
|
||||
assert set(cmake.compiler_flags["cflags"]) == set(["-O3"])
|
||||
assert set(client.compiler_flags["fflags"]) == set(["-O0", "-g"])
|
||||
assert set(client.compiler_flags["cflags"]) == {"-O0", "-g"}
|
||||
assert set(cmake.compiler_flags["cflags"]) == {"-O3"}
|
||||
assert set(client.compiler_flags["fflags"]) == {"-O0", "-g"}
|
||||
assert not set(cmake.compiler_flags["fflags"])
|
||||
|
||||
@pytest.mark.regression("9908")
|
||||
def test_spec_flags_maintain_order(self, mutable_config, gcc11_with_flags):
|
||||
"""Tests that Spack assembles flags in a consistent way (i.e. with the same ordering),
|
||||
for successive concretizations.
|
||||
"""
|
||||
mutable_config.set("compilers", [gcc11_with_flags])
|
||||
spec_str = "libelf %gcc@11.1.0 os=redhat6"
|
||||
for _ in range(3):
|
||||
s = Spec(spec_str).concretized()
|
||||
assert all(
|
||||
s.compiler_flags[x] == ["-O0", "-g"] for x in ("cflags", "cxxflags", "fflags")
|
||||
)
|
||||
|
||||
@pytest.mark.xfail(reason="Broken, needs to be fixed")
|
||||
def test_compiler_flags_from_compiler_and_dependent(self):
|
||||
client = Spec("cmake-client %clang@12.2.0 platform=test os=fe target=fe cflags==-g")
|
||||
client.concretize()
|
||||
@ -333,9 +368,10 @@ def test_compiler_flags_from_compiler_and_dependent(self):
|
||||
for spec in [client, cmake]:
|
||||
assert spec.compiler_flags["cflags"] == ["-O3", "-g"]
|
||||
|
||||
def test_compiler_flags_differ_identical_compilers(self):
|
||||
def test_compiler_flags_differ_identical_compilers(self, mutable_config, clang12_with_flags):
|
||||
mutable_config.set("compilers", [clang12_with_flags])
|
||||
# Correct arch to use test compiler that has flags
|
||||
spec = Spec("a %clang@12.2.0 platform=test os=fe target=fe")
|
||||
spec = Spec("pkg-a %clang@12.2.0 platform=test os=fe target=fe")
|
||||
|
||||
# Get the compiler that matches the spec (
|
||||
compiler = spack.compilers.compiler_for_spec("clang@=12.2.0", spec.architecture)
|
||||
@ -349,6 +385,9 @@ def test_compiler_flags_differ_identical_compilers(self):
|
||||
spec.concretize()
|
||||
assert spec.satisfies("cflags=-O2")
|
||||
|
||||
@pytest.mark.only_clingo(
|
||||
"Optional compiler propagation isn't deprecated for original concretizer"
|
||||
)
|
||||
def test_concretize_compiler_flag_propagate(self):
|
||||
spec = Spec("hypre cflags=='-g' ^openblas")
|
||||
spec.concretize()
|
||||
@ -385,28 +424,23 @@ def test_compiler_inherited_upwards(self):
|
||||
for dep in spec.traverse():
|
||||
assert "%clang" in dep
|
||||
|
||||
def test_architecture_inheritance(self):
|
||||
"""test_architecture_inheritance is likely to fail with an
|
||||
UnavailableCompilerVersionError if the architecture is concretized
|
||||
incorrectly.
|
||||
"""
|
||||
spec = Spec("cmake-client %gcc@11.1.0 os=fe ^ cmake")
|
||||
spec.concretize()
|
||||
assert spec["cmake"].architecture == spec.architecture
|
||||
|
||||
@pytest.mark.only_clingo("Fixing the parser broke this test for the original concretizer")
|
||||
def test_architecture_deep_inheritance(self, mock_targets):
|
||||
def test_architecture_deep_inheritance(self, mock_targets, compiler_factory):
|
||||
"""Make sure that indirect dependencies receive architecture
|
||||
information from the root even when partial architecture information
|
||||
is provided by an intermediate dependency.
|
||||
"""
|
||||
spec_str = "mpileaks %gcc@4.5.0 os=CNL target=nocona" " ^dyninst os=CNL ^callpath os=CNL"
|
||||
spec = Spec(spec_str).concretized()
|
||||
for s in spec.traverse(root=False):
|
||||
assert s.architecture.target == spec.architecture.target
|
||||
cnl_compiler = compiler_factory(spec="gcc@4.5.0", operating_system="CNL")
|
||||
# CNL compiler has no target attribute, and this is essential to make detection pass
|
||||
del cnl_compiler["compiler"]["target"]
|
||||
with spack.config.override("compilers", [cnl_compiler]):
|
||||
spec_str = "mpileaks %gcc@4.5.0 os=CNL target=nocona ^dyninst os=CNL ^callpath os=CNL"
|
||||
spec = Spec(spec_str).concretized()
|
||||
for s in spec.traverse(root=False):
|
||||
assert s.architecture.target == spec.architecture.target
|
||||
|
||||
def test_compiler_flags_from_user_are_grouped(self):
|
||||
spec = Spec('a%gcc cflags="-O -foo-flag foo-val" platform=test')
|
||||
spec = Spec('pkg-a%gcc cflags="-O -foo-flag foo-val" platform=test')
|
||||
spec.concretize()
|
||||
cflags = spec.compiler_flags["cflags"]
|
||||
assert any(x == "-foo-flag foo-val" for x in cflags)
|
||||
@ -458,19 +492,54 @@ def test_concretize_two_virtuals_with_dual_provider_and_a_conflict(self):
|
||||
@pytest.mark.only_clingo(
|
||||
"Optional compiler propagation isn't deprecated for original concretizer"
|
||||
)
|
||||
def test_concretize_propagate_disabled_variant(self):
|
||||
"""Test a package variant value was passed from its parent."""
|
||||
spec = Spec("hypre~~shared ^openblas")
|
||||
spec.concretize()
|
||||
|
||||
assert spec.satisfies("^openblas~shared")
|
||||
@pytest.mark.parametrize(
|
||||
"spec_str,expected_propagation",
|
||||
[
|
||||
("hypre~~shared ^openblas+shared", [("hypre", "~shared"), ("openblas", "+shared")]),
|
||||
# Propagates past a node that doesn't have the variant
|
||||
("hypre~~shared ^openblas", [("hypre", "~shared"), ("openblas", "~shared")]),
|
||||
(
|
||||
"ascent~~shared +adios2",
|
||||
[("ascent", "~shared"), ("adios2", "~shared"), ("bzip2", "~shared")],
|
||||
),
|
||||
# Propagates below a node that uses the other value explicitly
|
||||
(
|
||||
"ascent~~shared +adios2 ^adios2+shared",
|
||||
[("ascent", "~shared"), ("adios2", "+shared"), ("bzip2", "~shared")],
|
||||
),
|
||||
(
|
||||
"ascent++shared +adios2 ^adios2~shared",
|
||||
[("ascent", "+shared"), ("adios2", "~shared"), ("bzip2", "+shared")],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_concretize_propagate_disabled_variant(self, spec_str, expected_propagation):
|
||||
"""Tests various patterns of boolean variant propagation"""
|
||||
spec = Spec(spec_str).concretized()
|
||||
for key, expected_satisfies in expected_propagation:
|
||||
spec[key].satisfies(expected_satisfies)
|
||||
|
||||
@pytest.mark.only_clingo(
|
||||
"Optional compiler propagation isn't deprecated for original concretizer"
|
||||
)
|
||||
def test_concretize_propagated_variant_is_not_passed_to_dependent(self):
|
||||
"""Test a package variant value was passed from its parent."""
|
||||
spec = Spec("hypre~~shared ^openblas+shared")
|
||||
spec = Spec("ascent~~shared +adios2 ^adios2+shared")
|
||||
spec.concretize()
|
||||
|
||||
assert spec.satisfies("^openblas+shared")
|
||||
assert spec.satisfies("^adios2+shared")
|
||||
assert spec.satisfies("^bzip2~shared")
|
||||
|
||||
@pytest.mark.only_clingo(
|
||||
"Optional compiler propagation isn't deprecated for original concretizer"
|
||||
)
|
||||
def test_concretize_propagate_specified_variant(self):
|
||||
"""Test that only the specified variant is propagated to the dependencies"""
|
||||
spec = Spec("parent-foo-bar ~~foo")
|
||||
spec.concretize()
|
||||
|
||||
assert spec.satisfies("~foo") and spec.satisfies("^dependency-foo-bar~foo")
|
||||
assert spec.satisfies("+bar") and not spec.satisfies("^dependency-foo-bar+bar")
|
||||
|
||||
@pytest.mark.only_clingo("Original concretizer is allowed to forego variant propagation")
|
||||
def test_concretize_propagate_multivalue_variant(self):
|
||||
@ -479,20 +548,20 @@ def test_concretize_propagate_multivalue_variant(self):
|
||||
spec = Spec("multivalue-variant foo==baz,fee")
|
||||
spec.concretize()
|
||||
|
||||
assert spec.satisfies("^a foo=baz,fee")
|
||||
assert spec.satisfies("^b foo=baz,fee")
|
||||
assert not spec.satisfies("^a foo=bar")
|
||||
assert not spec.satisfies("^b foo=bar")
|
||||
assert spec.satisfies("^pkg-a foo=baz,fee")
|
||||
assert spec.satisfies("^pkg-b foo=baz,fee")
|
||||
assert not spec.satisfies("^pkg-a foo=bar")
|
||||
assert not spec.satisfies("^pkg-b foo=bar")
|
||||
|
||||
def test_no_matching_compiler_specs(self, mock_low_high_config):
|
||||
# only relevant when not building compilers as needed
|
||||
with spack.concretize.enable_compiler_existence_check():
|
||||
s = Spec("a %gcc@=0.0.0")
|
||||
s = Spec("pkg-a %gcc@=0.0.0")
|
||||
with pytest.raises(spack.concretize.UnavailableCompilerVersionError):
|
||||
s.concretize()
|
||||
|
||||
def test_no_compilers_for_arch(self):
|
||||
s = Spec("a arch=linux-rhel0-x86_64")
|
||||
s = Spec("pkg-a arch=linux-rhel0-x86_64")
|
||||
with pytest.raises(spack.error.SpackError):
|
||||
s.concretize()
|
||||
|
||||
@ -701,7 +770,7 @@ def test_regression_issue_7941(self):
|
||||
# The string representation of a spec containing
|
||||
# an explicit multi-valued variant and a dependency
|
||||
# might be parsed differently than the originating spec
|
||||
s = Spec("a foobar=bar ^b")
|
||||
s = Spec("pkg-a foobar=bar ^pkg-b")
|
||||
t = Spec(str(s))
|
||||
|
||||
s.concretize()
|
||||
@ -803,9 +872,12 @@ def test_concretize_anonymous_dep(self, spec_str):
|
||||
],
|
||||
)
|
||||
@pytest.mark.only_clingo("Original concretizer cannot work around conflicts")
|
||||
def test_compiler_conflicts_in_package_py(self, spec_str, expected_str):
|
||||
s = Spec(spec_str).concretized()
|
||||
assert s.satisfies(expected_str)
|
||||
def test_compiler_conflicts_in_package_py(
|
||||
self, spec_str, expected_str, clang12_with_flags, gcc11_with_flags
|
||||
):
|
||||
with spack.config.override("compilers", [clang12_with_flags, gcc11_with_flags]):
|
||||
s = Spec(spec_str).concretized()
|
||||
assert s.satisfies(expected_str)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"spec_str,expected,unexpected",
|
||||
@ -1070,14 +1142,14 @@ def test_conditional_provides_or_depends_on(self):
|
||||
[
|
||||
# Check that True is treated correctly and attaches test deps
|
||||
# to all nodes in the DAG
|
||||
("a", True, ["a"], []),
|
||||
("a foobar=bar", True, ["a", "b"], []),
|
||||
("pkg-a", True, ["pkg-a"], []),
|
||||
("pkg-a foobar=bar", True, ["pkg-a", "pkg-b"], []),
|
||||
# Check that a list of names activates the dependency only for
|
||||
# packages in that list
|
||||
("a foobar=bar", ["a"], ["a"], ["b"]),
|
||||
("a foobar=bar", ["b"], ["b"], ["a"]),
|
||||
("pkg-a foobar=bar", ["pkg-a"], ["pkg-a"], ["pkg-b"]),
|
||||
("pkg-a foobar=bar", ["pkg-b"], ["pkg-b"], ["pkg-a"]),
|
||||
# Check that False disregard test dependencies
|
||||
("a foobar=bar", False, [], ["a", "b"]),
|
||||
("pkg-a foobar=bar", False, [], ["pkg-a", "pkg-b"]),
|
||||
],
|
||||
)
|
||||
def test_activating_test_dependencies(self, spec_str, tests_arg, with_dep, without_dep):
|
||||
@ -1095,16 +1167,18 @@ def test_activating_test_dependencies(self, spec_str, tests_arg, with_dep, witho
|
||||
|
||||
@pytest.mark.regression("20019")
|
||||
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
|
||||
def test_compiler_match_is_preferred_to_newer_version(self):
|
||||
def test_compiler_match_is_preferred_to_newer_version(self, compiler_factory):
|
||||
# This spec depends on openblas. Openblas has a conflict
|
||||
# that doesn't allow newer versions with gcc@4.4.0. Check
|
||||
# that an old version of openblas is selected, rather than
|
||||
# a different compiler for just that node.
|
||||
spec_str = "simple-inheritance+openblas %gcc@10.1.0 os=redhat6"
|
||||
s = Spec(spec_str).concretized()
|
||||
|
||||
assert "openblas@0.2.15" in s
|
||||
assert s["openblas"].satisfies("%gcc@10.1.0")
|
||||
with spack.config.override(
|
||||
"compilers", [compiler_factory(spec="gcc@10.1.0", operating_system="redhat6")]
|
||||
):
|
||||
spec_str = "simple-inheritance+openblas %gcc@10.1.0 os=redhat6"
|
||||
s = Spec(spec_str).concretized()
|
||||
assert "openblas@0.2.15" in s
|
||||
assert s["openblas"].satisfies("%gcc@10.1.0")
|
||||
|
||||
@pytest.mark.regression("19981")
|
||||
def test_target_ranges_in_conflicts(self):
|
||||
@ -1129,8 +1203,12 @@ def test_variant_not_default(self):
|
||||
|
||||
@pytest.mark.regression("20055")
|
||||
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
|
||||
def test_custom_compiler_version(self):
|
||||
s = Spec("a %gcc@10foo os=redhat6").concretized()
|
||||
def test_custom_compiler_version(self, mutable_config, compiler_factory, monkeypatch):
|
||||
mutable_config.set(
|
||||
"compilers", [compiler_factory(spec="gcc@10foo", operating_system="redhat6")]
|
||||
)
|
||||
monkeypatch.setattr(spack.compiler.Compiler, "real_version", "10.2.1")
|
||||
s = Spec("pkg-a %gcc@10foo os=redhat6").concretized()
|
||||
assert "%gcc@10foo" in s
|
||||
|
||||
def test_all_patches_applied(self):
|
||||
@ -1255,10 +1333,10 @@ def test_reuse_installed_packages_when_package_def_changes(
|
||||
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
|
||||
def test_reuse_with_flags(self, mutable_database, mutable_config):
|
||||
spack.config.set("concretizer:reuse", True)
|
||||
spec = Spec("a cflags=-g cxxflags=-g").concretized()
|
||||
spec = Spec("pkg-a cflags=-g cxxflags=-g").concretized()
|
||||
spack.store.STORE.db.add(spec, None)
|
||||
|
||||
testspec = Spec("a cflags=-g")
|
||||
testspec = Spec("pkg-a cflags=-g")
|
||||
testspec.concretize()
|
||||
assert testspec == spec
|
||||
|
||||
@ -1329,11 +1407,16 @@ def test_external_with_non_default_variant_as_dependency(self):
|
||||
("mpileaks%gcc@10.2.1 platform=test os=redhat6", "os=redhat6"),
|
||||
],
|
||||
)
|
||||
def test_os_selection_when_multiple_choices_are_possible(self, spec_str, expected_os):
|
||||
s = Spec(spec_str).concretized()
|
||||
|
||||
for node in s.traverse():
|
||||
assert node.satisfies(expected_os)
|
||||
def test_os_selection_when_multiple_choices_are_possible(
|
||||
self, spec_str, expected_os, compiler_factory
|
||||
):
|
||||
# GCC 10.2.1 is defined both for debian and for redhat
|
||||
with spack.config.override(
|
||||
"compilers", [compiler_factory(spec="gcc@10.2.1", operating_system="redhat6")]
|
||||
):
|
||||
s = Spec(spec_str).concretized()
|
||||
for node in s.traverse():
|
||||
assert node.satisfies(expected_os)
|
||||
|
||||
@pytest.mark.regression("22718")
|
||||
@pytest.mark.parametrize(
|
||||
@ -1461,6 +1544,30 @@ def test_sticky_variant_in_package(self):
|
||||
s = Spec("sticky-variant %clang").concretized()
|
||||
assert s.satisfies("%clang") and s.satisfies("~allow-gcc")
|
||||
|
||||
@pytest.mark.regression("42172")
|
||||
@pytest.mark.only_clingo("Original concretizer cannot use sticky variants")
|
||||
@pytest.mark.parametrize(
|
||||
"spec,allow_gcc",
|
||||
[
|
||||
("sticky-variant@1.0+allow-gcc", True),
|
||||
("sticky-variant@1.0~allow-gcc", False),
|
||||
("sticky-variant@1.0", False),
|
||||
],
|
||||
)
|
||||
def test_sticky_variant_in_external(self, spec, allow_gcc):
|
||||
# setup external for sticky-variant+allow-gcc
|
||||
config = {"externals": [{"spec": spec, "prefix": "/fake/path"}], "buildable": False}
|
||||
spack.config.set("packages:sticky-variant", config)
|
||||
|
||||
maybe = llnl.util.lang.nullcontext if allow_gcc else pytest.raises
|
||||
with maybe(spack.error.SpackError):
|
||||
s = Spec("sticky-variant-dependent%gcc").concretized()
|
||||
|
||||
if allow_gcc:
|
||||
assert s.satisfies("%gcc")
|
||||
assert s["sticky-variant"].satisfies("+allow-gcc")
|
||||
assert s["sticky-variant"].external
|
||||
|
||||
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
|
||||
def test_do_not_invent_new_concrete_versions_unless_necessary(self):
|
||||
# ensure we select a known satisfying version rather than creating
|
||||
@ -1566,33 +1673,35 @@ 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()
|
||||
s = Spec("pkg-c").concretized()
|
||||
assert s.namespace != "builtin.mock"
|
||||
s.package.do_install(fake=True, explicit=True)
|
||||
|
||||
with spack.config.override("concretizer:reuse", True):
|
||||
s = Spec("c").concretized()
|
||||
s = Spec("pkg-c").concretized()
|
||||
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")
|
||||
builder.add_package("c")
|
||||
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("pkg-c")
|
||||
with spack.repo.use_repositories(builder.root, override=False):
|
||||
s = Spec("c").concretized()
|
||||
s = Spec("pkg-c").concretized()
|
||||
assert s.namespace == "myrepo"
|
||||
s.package.do_install(fake=True, explicit=True)
|
||||
|
||||
del sys.modules["spack.pkg.myrepo.c"]
|
||||
del sys.modules["spack.pkg.myrepo.pkg-c"]
|
||||
del sys.modules["spack.pkg.myrepo"]
|
||||
builder.remove("c")
|
||||
builder.remove("pkg-c")
|
||||
with spack.repo.use_repositories(builder.root, override=False) as repos:
|
||||
# TODO (INJECT CONFIGURATION): unclear why the cache needs to be invalidated explicitly
|
||||
repos.repos[0]._pkg_checker.invalidate()
|
||||
with spack.config.override("concretizer:reuse", True):
|
||||
s = Spec("c").concretized()
|
||||
s = Spec("pkg-c").concretized()
|
||||
assert s.namespace == "builtin.mock"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -1704,20 +1813,20 @@ def test_misleading_error_message_on_version(self, mutable_database):
|
||||
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
|
||||
def test_version_weight_and_provenance(self):
|
||||
"""Test package preferences during coconcretization."""
|
||||
reusable_specs = [Spec(spec_str).concretized() for spec_str in ("b@0.9", "b@1.0")]
|
||||
root_spec = Spec("a foobar=bar")
|
||||
reusable_specs = [Spec(spec_str).concretized() for spec_str in ("pkg-b@0.9", "pkg-b@1.0")]
|
||||
root_spec = Spec("pkg-a foobar=bar")
|
||||
|
||||
with spack.config.override("concretizer:reuse", True):
|
||||
solver = spack.solver.asp.Solver()
|
||||
setup = spack.solver.asp.SpackSolverSetup()
|
||||
result, _, _ = solver.driver.solve(setup, [root_spec], reuse=reusable_specs)
|
||||
# The result here should have a single spec to build ('a')
|
||||
# and it should be using b@1.0 with a version badness of 2
|
||||
# The result here should have a single spec to build ('pkg-a')
|
||||
# and it should be using pkg-b@1.0 with a version badness of 2
|
||||
# The provenance is:
|
||||
# version_declared("b","1.0",0,"package_py").
|
||||
# version_declared("b","0.9",1,"package_py").
|
||||
# version_declared("b","1.0",2,"installed").
|
||||
# version_declared("b","0.9",3,"installed").
|
||||
# version_declared("pkg-b","1.0",0,"package_py").
|
||||
# version_declared("pkg-b","0.9",1,"package_py").
|
||||
# version_declared("pkg-b","1.0",2,"installed").
|
||||
# version_declared("pkg-b","0.9",3,"installed").
|
||||
#
|
||||
# Depending on the target, it may also use gnuconfig
|
||||
result_spec = result.specs[0]
|
||||
@ -1730,12 +1839,12 @@ def test_version_weight_and_provenance(self):
|
||||
|
||||
for criterion in criteria:
|
||||
assert criterion in result.criteria
|
||||
assert result_spec.satisfies("^b@1.0")
|
||||
assert result_spec.satisfies("^pkg-b@1.0")
|
||||
|
||||
@pytest.mark.regression("31169")
|
||||
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
|
||||
def test_not_reusing_incompatible_os_or_compiler(self):
|
||||
root_spec = Spec("b")
|
||||
root_spec = Spec("pkg-b")
|
||||
s = root_spec.concretized()
|
||||
wrong_compiler, wrong_os = s.copy(), s.copy()
|
||||
wrong_compiler.compiler = spack.spec.CompilerSpec("gcc@12.1.0")
|
||||
@ -1990,7 +2099,7 @@ def test_external_python_extension_find_unified_python(self):
|
||||
"specs",
|
||||
[
|
||||
["mpileaks^ callpath ^dyninst@8.1.1:8 ^mpich2@1.3:1"],
|
||||
["multivalue-variant ^a@2:2"],
|
||||
["multivalue-variant ^pkg-a@2:2"],
|
||||
["v1-consumer ^conditional-provider@1:1 +disable-v1"],
|
||||
],
|
||||
)
|
||||
@ -2040,7 +2149,7 @@ def test_compiler_match_constraints_when_selected(self):
|
||||
},
|
||||
]
|
||||
spack.config.set("compilers", compiler_configuration)
|
||||
s = Spec("a %gcc@:11").concretized()
|
||||
s = Spec("pkg-a %gcc@:11").concretized()
|
||||
assert s.compiler.version == ver("=11.1.0"), s
|
||||
|
||||
@pytest.mark.regression("36339")
|
||||
@ -2061,7 +2170,7 @@ def test_compiler_with_custom_non_numeric_version(self, mock_executable):
|
||||
}
|
||||
]
|
||||
spack.config.set("compilers", compiler_configuration)
|
||||
s = Spec("a %gcc@foo").concretized()
|
||||
s = Spec("pkg-a %gcc@foo").concretized()
|
||||
assert s.compiler.version == ver("=foo")
|
||||
|
||||
@pytest.mark.regression("36628")
|
||||
@ -2087,7 +2196,7 @@ def test_concretization_with_compilers_supporting_target_any(self):
|
||||
]
|
||||
|
||||
with spack.config.override("compilers", compiler_configuration):
|
||||
s = spack.spec.Spec("a").concretized()
|
||||
s = Spec("pkg-a").concretized()
|
||||
assert s.satisfies("%gcc@12.1.0")
|
||||
|
||||
@pytest.mark.parametrize("spec_str", ["mpileaks", "mpileaks ^mpich"])
|
||||
@ -2122,7 +2231,7 @@ def test_dont_define_new_version_from_input_if_checksum_required(self, working_e
|
||||
with pytest.raises(spack.error.UnsatisfiableSpecError):
|
||||
# normally spack concretizes to @=3.0 if it's not defined in package.py, except
|
||||
# when checksums are required
|
||||
Spec("a@=3.0").concretized()
|
||||
Spec("pkg-a@=3.0").concretized()
|
||||
|
||||
@pytest.mark.regression("39570")
|
||||
@pytest.mark.db
|
||||
@ -2142,6 +2251,33 @@ def test_reuse_python_from_cli_and_extension_from_db(self, mutable_database):
|
||||
|
||||
assert with_reuse.dag_hash() == without_reuse.dag_hash()
|
||||
|
||||
@pytest.mark.regression("35536")
|
||||
@pytest.mark.parametrize(
|
||||
"spec_str,expected_namespaces",
|
||||
[
|
||||
# Single node with fully qualified namespace
|
||||
("builtin.mock.gmake", {"gmake": "builtin.mock"}),
|
||||
# Dependency with fully qualified namespace
|
||||
("hdf5 ^builtin.mock.gmake", {"gmake": "builtin.mock", "hdf5": "duplicates.test"}),
|
||||
("hdf5 ^gmake", {"gmake": "duplicates.test", "hdf5": "duplicates.test"}),
|
||||
],
|
||||
)
|
||||
@pytest.mark.only_clingo("Uses specs requiring multiple gmake specs")
|
||||
def test_select_lower_priority_package_from_repository_stack(
|
||||
self, spec_str, expected_namespaces
|
||||
):
|
||||
"""Tests that a user can explicitly select a lower priority, fully qualified dependency
|
||||
from cli.
|
||||
"""
|
||||
# 'builtin.mock" and "duplicates.test" share a 'gmake' package
|
||||
additional_repo = os.path.join(spack.paths.repos_path, "duplicates.test")
|
||||
with spack.repo.use_repositories(additional_repo, override=False):
|
||||
s = Spec(spec_str).concretized()
|
||||
|
||||
for name, namespace in expected_namespaces.items():
|
||||
assert s[name].concrete
|
||||
assert s[name].namespace == namespace
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def duplicates_test_repository():
|
||||
@ -2320,7 +2456,9 @@ def test_drop_moving_targets(v_str, v_opts, checksummed):
|
||||
class TestConcreteSpecsByHash:
|
||||
"""Tests the container of concrete specs"""
|
||||
|
||||
@pytest.mark.parametrize("input_specs", [["a"], ["a foobar=bar", "b"], ["a foobar=baz", "b"]])
|
||||
@pytest.mark.parametrize(
|
||||
"input_specs", [["pkg-a"], ["pkg-a foobar=bar", "pkg-b"], ["pkg-a foobar=baz", "pkg-b"]]
|
||||
)
|
||||
def test_adding_specs(self, input_specs, default_mock_concretization):
|
||||
"""Tests that concrete specs in the container are equivalent, but stored as different
|
||||
objects in memory.
|
||||
|
@ -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 = {
|
||||
|
@ -105,7 +105,7 @@ def test_preferred_variants_from_wildcard(self):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"compiler_str,spec_str",
|
||||
[("gcc@=4.5.0", "mpileaks"), ("clang@=12.0.0", "mpileaks"), ("gcc@=4.5.0", "openmpi")],
|
||||
[("gcc@=4.8.0", "mpileaks"), ("clang@=12.0.0", "mpileaks"), ("gcc@=4.8.0", "openmpi")],
|
||||
)
|
||||
def test_preferred_compilers(self, compiler_str, spec_str):
|
||||
"""Test preferred compilers are applied correctly"""
|
||||
@ -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("pkg-a").concretized()
|
||||
assert s.satisfies("foo=bar")
|
||||
|
@ -872,6 +872,8 @@ def test_skip_requirement_when_default_requirement_condition_cannot_be_met(
|
||||
|
||||
def test_requires_directive(concretize_scope, mock_packages):
|
||||
compilers_yaml = pathlib.Path(concretize_scope) / "compilers.yaml"
|
||||
|
||||
# NOTE: target is omitted here so that the test works on aarch64, as well.
|
||||
compilers_yaml.write_text(
|
||||
"""
|
||||
compilers::
|
||||
@ -883,7 +885,6 @@ def test_requires_directive(concretize_scope, mock_packages):
|
||||
f77: null
|
||||
fc: null
|
||||
operating_system: debian6
|
||||
target: x86_64
|
||||
modules: []
|
||||
"""
|
||||
)
|
||||
@ -896,3 +897,69 @@ def test_requires_directive(concretize_scope, mock_packages):
|
||||
# This package can only be compiled with clang
|
||||
with pytest.raises(spack.error.SpackError, match="can only be compiled with Clang"):
|
||||
Spec("requires_clang").concretized()
|
||||
|
||||
|
||||
@pytest.mark.regression("42084")
|
||||
def test_requiring_package_on_multiple_virtuals(concretize_scope, mock_packages):
|
||||
update_packages_config(
|
||||
"""
|
||||
packages:
|
||||
all:
|
||||
providers:
|
||||
scalapack: [netlib-scalapack]
|
||||
blas:
|
||||
require: intel-parallel-studio
|
||||
lapack:
|
||||
require: intel-parallel-studio
|
||||
scalapack:
|
||||
require: intel-parallel-studio
|
||||
"""
|
||||
)
|
||||
s = Spec("dla-future").concretized()
|
||||
|
||||
assert s["blas"].name == "intel-parallel-studio"
|
||||
assert s["lapack"].name == "intel-parallel-studio"
|
||||
assert s["scalapack"].name == "intel-parallel-studio"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"spec_str,expected,not_expected",
|
||||
[
|
||||
(
|
||||
"forward-multi-value +cuda cuda_arch=10 ^dependency-mv~cuda",
|
||||
["cuda_arch=10", "^dependency-mv~cuda"],
|
||||
["cuda_arch=11", "^dependency-mv cuda_arch=10", "^dependency-mv cuda_arch=11"],
|
||||
),
|
||||
(
|
||||
"forward-multi-value +cuda cuda_arch=10 ^dependency-mv+cuda",
|
||||
["cuda_arch=10", "^dependency-mv cuda_arch=10"],
|
||||
["cuda_arch=11", "^dependency-mv cuda_arch=11"],
|
||||
),
|
||||
(
|
||||
"forward-multi-value +cuda cuda_arch=11 ^dependency-mv+cuda",
|
||||
["cuda_arch=11", "^dependency-mv cuda_arch=11"],
|
||||
["cuda_arch=10", "^dependency-mv cuda_arch=10"],
|
||||
),
|
||||
(
|
||||
"forward-multi-value +cuda cuda_arch=10,11 ^dependency-mv+cuda",
|
||||
["cuda_arch=10,11", "^dependency-mv cuda_arch=10,11"],
|
||||
[],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_forward_multi_valued_variant_using_requires(
|
||||
spec_str, expected, not_expected, config, mock_packages
|
||||
):
|
||||
"""Tests that a package can forward multivalue variants to dependencies, using
|
||||
`requires` directives of the form:
|
||||
|
||||
for _val in ("shared", "static"):
|
||||
requires(f"^some-virtual-mv libs={_val}", when=f"libs={_val} ^some-virtual-mv")
|
||||
"""
|
||||
s = Spec(spec_str).concretized()
|
||||
|
||||
for constraint in expected:
|
||||
assert s.satisfies(constraint)
|
||||
|
||||
for constraint in not_expected:
|
||||
assert not s.satisfies(constraint)
|
||||
|
@ -1176,13 +1176,13 @@ def test_license_dir_config(mutable_config, mock_packages):
|
||||
expected_dir = spack.paths.default_license_dir
|
||||
assert spack.config.get("config:license_dir") == expected_dir
|
||||
assert spack.package_base.PackageBase.global_license_dir == expected_dir
|
||||
assert spack.repo.PATH.get_pkg_class("a").global_license_dir == expected_dir
|
||||
assert spack.repo.PATH.get_pkg_class("pkg-a").global_license_dir == expected_dir
|
||||
|
||||
rel_path = os.path.join(os.path.sep, "foo", "bar", "baz")
|
||||
spack.config.set("config:license_dir", rel_path)
|
||||
assert spack.config.get("config:license_dir") == rel_path
|
||||
assert spack.package_base.PackageBase.global_license_dir == rel_path
|
||||
assert spack.repo.PATH.get_pkg_class("a").global_license_dir == rel_path
|
||||
assert spack.repo.PATH.get_pkg_class("pkg-a").global_license_dir == rel_path
|
||||
|
||||
|
||||
@pytest.mark.regression("22547")
|
||||
@ -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):
|
||||
|
@ -21,6 +21,7 @@
|
||||
import py
|
||||
import pytest
|
||||
|
||||
import archspec.cpu
|
||||
import archspec.cpu.microarchitecture
|
||||
import archspec.cpu.schema
|
||||
|
||||
@ -596,7 +597,7 @@ def mutable_mock_repo(mock_repo_path, request):
|
||||
def mock_custom_repository(tmpdir, mutable_mock_repo):
|
||||
"""Create a custom repository with a single package "c" and return its path."""
|
||||
builder = spack.repo.MockRepositoryBuilder(tmpdir.mkdir("myrepo"))
|
||||
builder.add_package("c")
|
||||
builder.add_package("pkg-c")
|
||||
return builder.root
|
||||
|
||||
|
||||
@ -629,7 +630,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.
|
||||
|
||||
@ -708,14 +709,13 @@ def configuration_dir(tmpdir_factory, linux_os):
|
||||
t.write(content)
|
||||
|
||||
compilers_yaml = test_config.join("compilers.yaml")
|
||||
content = "".join(compilers_yaml.read()).format(linux_os)
|
||||
content = "".join(compilers_yaml.read()).format(
|
||||
linux_os=linux_os, target=str(archspec.cpu.host().family)
|
||||
)
|
||||
t = tmpdir.join("site", "compilers.yaml")
|
||||
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`."""
|
||||
@ -1953,17 +1953,50 @@ def pytest_runtest_setup(item):
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def disable_parallel_buildcache_push(monkeypatch):
|
||||
class MockPool:
|
||||
def map(self, func, args):
|
||||
return [func(a) for a in args]
|
||||
"""Disable process pools in tests."""
|
||||
monkeypatch.setattr(spack.cmd.buildcache, "_make_pool", spack.cmd.buildcache.NoPool)
|
||||
|
||||
def starmap(self, func, args):
|
||||
return [func(*a) for a in args]
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
def create_test_repo(tmpdir, pkg_name_content_tuples):
|
||||
repo_path = str(tmpdir)
|
||||
repo_yaml = tmpdir.join("repo.yaml")
|
||||
with open(str(repo_yaml), "w") as f:
|
||||
f.write(
|
||||
"""\
|
||||
repo:
|
||||
namespace: testcfgrequirements
|
||||
"""
|
||||
)
|
||||
|
||||
def __exit__(self, *args):
|
||||
pass
|
||||
packages_dir = tmpdir.join("packages")
|
||||
for pkg_name, pkg_str in pkg_name_content_tuples:
|
||||
pkg_dir = packages_dir.ensure(pkg_name, dir=True)
|
||||
pkg_file = pkg_dir.join("package.py")
|
||||
with open(str(pkg_file), "w") as f:
|
||||
f.write(pkg_str)
|
||||
|
||||
monkeypatch.setattr(spack.cmd.buildcache, "_make_pool", MockPool)
|
||||
return spack.repo.Repo(repo_path)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def compiler_factory():
|
||||
"""Factory for a compiler dict, taking a spec and an OS as arguments."""
|
||||
|
||||
def _factory(*, spec, operating_system):
|
||||
return {
|
||||
"compiler": {
|
||||
"spec": spec,
|
||||
"operating_system": operating_system,
|
||||
"paths": {"cc": "/path/to/cc", "cxx": "/path/to/cxx", "f77": None, "fc": None},
|
||||
"modules": [],
|
||||
"target": str(archspec.cpu.host().family),
|
||||
}
|
||||
}
|
||||
|
||||
return _factory
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def host_architecture_str():
|
||||
"""Returns the broad architecture family (x86_64, aarch64, etc.)"""
|
||||
return str(archspec.cpu.host().family)
|
||||
|
@ -1,353 +1,41 @@
|
||||
compilers:
|
||||
- compiler:
|
||||
spec: clang@3.3
|
||||
operating_system: {0.name}{0.version}
|
||||
spec: gcc@=4.8.0
|
||||
operating_system: {linux_os.name}{linux_os.version}
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: []
|
||||
target: {target}
|
||||
- compiler:
|
||||
spec: gcc@=4.8.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: []
|
||||
target: {target}
|
||||
- compiler:
|
||||
spec: clang@=12.0.0
|
||||
operating_system: {linux_os.name}{linux_os.version}
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
modules: []
|
||||
target: {target}
|
||||
- compiler:
|
||||
spec: gcc@4.5.0
|
||||
operating_system: {0.name}{0.version}
|
||||
spec: gcc@=10.2.1
|
||||
operating_system: {linux_os.name}{linux_os.version}
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@4.5.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: clang@3.3
|
||||
operating_system: CNL
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
- compiler:
|
||||
spec: clang@3.3
|
||||
operating_system: SuSE11
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: clang@3.3
|
||||
operating_system: yosemite
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: /path/to/gfortran
|
||||
fc: /path/to/gfortran
|
||||
operating_system: CNL
|
||||
spec: gcc@4.5.0
|
||||
modules: 'None'
|
||||
- compiler:
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: /path/to/gfortran
|
||||
fc: /path/to/gfortran
|
||||
operating_system: SuSE11
|
||||
spec: gcc@4.5.0
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: /path/to/gfortran
|
||||
fc: /path/to/gfortran
|
||||
operating_system: yosemite
|
||||
spec: gcc@4.5.0
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: /path/to/gfortran
|
||||
fc: /path/to/gfortran
|
||||
operating_system: elcapitan
|
||||
spec: gcc@4.5.0
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: clang@3.3
|
||||
operating_system: elcapitan
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@4.7.2
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc472
|
||||
cxx: /path/to/g++472
|
||||
f77: /path/to/gfortran472
|
||||
fc: /path/to/gfortran472
|
||||
flags:
|
||||
cflags: -O0 -g
|
||||
cxxflags: -O0 -g
|
||||
fflags: -O0 -g
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@4.4.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc440
|
||||
cxx: /path/to/g++440
|
||||
f77: /path/to/gfortran440
|
||||
fc: /path/to/gfortran440
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: clang@3.5
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/clang35
|
||||
cxx: /path/to/clang++35
|
||||
f77: None
|
||||
fc: None
|
||||
flags:
|
||||
cflags: -O3
|
||||
cxxflags: -O3
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: clang@8.0.0
|
||||
operating_system: redhat7
|
||||
paths:
|
||||
cc: /path/to/clang-8
|
||||
cxx: /path/to/clang++-8
|
||||
f77: /path/to/gfortran-9
|
||||
fc: /path/to/gfortran-9
|
||||
flags:
|
||||
cflags: -O3
|
||||
cxxflags: -O3
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: apple-clang@9.1.0
|
||||
operating_system: elcapitan
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@10foo
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: /path/to/gfortran
|
||||
fc: /path/to/gfortran
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@4.4.0-special
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: /path/to/gfortran
|
||||
fc: /path/to/gfortran
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: clang@12.0.0
|
||||
operating_system: {0.name}{0.version}
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: aarch64
|
||||
- compiler:
|
||||
spec: gcc@10.2.1
|
||||
operating_system: {0.name}{0.version}
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: aarch64
|
||||
- compiler:
|
||||
spec: clang@12.0.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: aarch64
|
||||
- compiler:
|
||||
spec: gcc@10.2.1
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: aarch64
|
||||
- compiler:
|
||||
spec: gcc@10.1.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: aarch64
|
||||
- compiler:
|
||||
spec: gcc@11.1.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
flags:
|
||||
cflags: -O0 -g
|
||||
cxxflags: -O0 -g
|
||||
fflags: -O0 -g
|
||||
modules: 'None'
|
||||
target: aarch64
|
||||
- compiler:
|
||||
spec: clang@12.2.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/clang35
|
||||
cxx: /path/to/clang++35
|
||||
f77: None
|
||||
fc: None
|
||||
flags:
|
||||
cflags: -O3
|
||||
cxxflags: -O3
|
||||
modules: 'None'
|
||||
target: aarch64
|
||||
- compiler:
|
||||
spec: gcc@10foo
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: /path/to/gfortran
|
||||
fc: /path/to/gfortran
|
||||
modules: 'None'
|
||||
target: aarch64
|
||||
- compiler:
|
||||
spec: clang@12.0.0
|
||||
operating_system: {0.name}{0.version}
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@10.2.1
|
||||
operating_system: {0.name}{0.version}
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: clang@12.0.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/clang
|
||||
cxx: /path/to/clang++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@10.2.1
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@10.1.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: gcc@11.1.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/gcc
|
||||
cxx: /path/to/g++
|
||||
f77: None
|
||||
fc: None
|
||||
flags:
|
||||
cflags: -O0 -g
|
||||
cxxflags: -O0 -g
|
||||
fflags: -O0 -g
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
- compiler:
|
||||
spec: clang@12.2.0
|
||||
operating_system: redhat6
|
||||
paths:
|
||||
cc: /path/to/clang35
|
||||
cxx: /path/to/clang++35
|
||||
f77: None
|
||||
fc: None
|
||||
flags:
|
||||
cflags: -O3
|
||||
cxxflags: -O3
|
||||
modules: 'None'
|
||||
target: x86_64
|
||||
modules: []
|
||||
target: {target}
|
||||
|
@ -1,14 +0,0 @@
|
||||
# DEPRECATED: remove this in v0.20
|
||||
# See `exclude.yaml` for the new syntax
|
||||
enable:
|
||||
- lmod
|
||||
lmod:
|
||||
core_compilers:
|
||||
- 'clang@3.3'
|
||||
hierarchy:
|
||||
- mpi
|
||||
blacklist:
|
||||
- callpath
|
||||
|
||||
all:
|
||||
autoload: direct
|
@ -1,30 +0,0 @@
|
||||
# DEPRECATED: remove this in v0.20
|
||||
# See `alter_environment.yaml` for the new syntax
|
||||
enable:
|
||||
- lmod
|
||||
lmod:
|
||||
core_compilers:
|
||||
- 'clang@3.3'
|
||||
|
||||
hierarchy:
|
||||
- mpi
|
||||
|
||||
all:
|
||||
autoload: none
|
||||
filter:
|
||||
environment_blacklist:
|
||||
- CMAKE_PREFIX_PATH
|
||||
environment:
|
||||
set:
|
||||
'{name}_ROOT': '{prefix}'
|
||||
|
||||
'platform=test target=x86_64':
|
||||
environment:
|
||||
set:
|
||||
FOO: 'foo'
|
||||
unset:
|
||||
- BAR
|
||||
|
||||
'platform=test target=core2':
|
||||
load:
|
||||
- 'foo/bar'
|
@ -1,12 +0,0 @@
|
||||
# DEPRECATED: remove this in v0.20
|
||||
# See `exclude.yaml` for the new syntax
|
||||
enable:
|
||||
- tcl
|
||||
tcl:
|
||||
whitelist:
|
||||
- zmpi
|
||||
blacklist:
|
||||
- callpath
|
||||
- mpi
|
||||
all:
|
||||
autoload: direct
|
@ -1,25 +0,0 @@
|
||||
# DEPRECATED: remove this in v0.20
|
||||
# See `alter_environment.yaml` for the new syntax
|
||||
enable:
|
||||
- tcl
|
||||
tcl:
|
||||
all:
|
||||
autoload: none
|
||||
filter:
|
||||
environment_blacklist:
|
||||
- CMAKE_PREFIX_PATH
|
||||
environment:
|
||||
set:
|
||||
'{name}_ROOT': '{prefix}'
|
||||
|
||||
'platform=test target=x86_64':
|
||||
environment:
|
||||
set:
|
||||
FOO: 'foo'
|
||||
OMPI_MCA_mpi_leave_pinned: '1'
|
||||
unset:
|
||||
- BAR
|
||||
|
||||
'platform=test target=core2':
|
||||
load:
|
||||
- 'foo/bar'
|
@ -1,8 +0,0 @@
|
||||
# DEPRECATED: remove this in v0.20
|
||||
# See `exclude_implicits.yaml` for the new syntax
|
||||
enable:
|
||||
- tcl
|
||||
tcl:
|
||||
blacklist_implicits: true
|
||||
all:
|
||||
autoload: direct
|
@ -4,7 +4,7 @@ tcl:
|
||||
all:
|
||||
autoload: none
|
||||
filter:
|
||||
environment_blacklist:
|
||||
exclude_env_vars:
|
||||
- CMAKE_PREFIX_PATH
|
||||
environment:
|
||||
set:
|
||||
|
@ -69,7 +69,7 @@ def test_spec_installed_upstream(
|
||||
|
||||
# a known installed spec should say that it's installed
|
||||
with spack.repo.use_repositories(mock_custom_repository):
|
||||
spec = spack.spec.Spec("c").concretized()
|
||||
spec = spack.spec.Spec("pkg-c").concretized()
|
||||
assert not spec.installed
|
||||
assert not spec.installed_upstream
|
||||
|
||||
@ -813,7 +813,7 @@ def test_query_virtual_spec(database):
|
||||
|
||||
def test_failed_spec_path_error(database):
|
||||
"""Ensure spec not concrete check is covered."""
|
||||
s = spack.spec.Spec("a")
|
||||
s = spack.spec.Spec("pkg-a")
|
||||
with pytest.raises(AssertionError, match="concrete spec required"):
|
||||
spack.store.STORE.failure_tracker.mark(s)
|
||||
|
||||
@ -828,7 +828,7 @@ def _is(self, spec):
|
||||
# Pretend the spec has been failure locked
|
||||
monkeypatch.setattr(spack.database.FailureTracker, "lock_taken", _is)
|
||||
|
||||
s = spack.spec.Spec("a").concretized()
|
||||
s = spack.spec.Spec("pkg-a").concretized()
|
||||
spack.store.STORE.failure_tracker.clear(s)
|
||||
out = capfd.readouterr()[0]
|
||||
assert "Retaining failure marking" in out
|
||||
@ -846,7 +846,7 @@ def _is(self, spec):
|
||||
# Ensure raise OSError when try to remove the non-existent marking
|
||||
monkeypatch.setattr(spack.database.FailureTracker, "persistent_mark", _is)
|
||||
|
||||
s = default_mock_concretization("a")
|
||||
s = spack.spec.Spec("pkg-a").concretized()
|
||||
spack.store.STORE.failure_tracker.clear(s, force=True)
|
||||
out = capfd.readouterr()[1]
|
||||
assert "Removing failure marking despite lock" in out
|
||||
@ -860,15 +860,16 @@ def test_mark_failed(default_mock_concretization, mutable_database, monkeypatch,
|
||||
def _raise_exc(lock):
|
||||
raise lk.LockTimeoutError("write", "/mock-lock", 1.234, 10)
|
||||
|
||||
# Ensure attempt to acquire write lock on the mark raises the exception
|
||||
monkeypatch.setattr(lk.Lock, "acquire_write", _raise_exc)
|
||||
|
||||
with tmpdir.as_cwd():
|
||||
s = default_mock_concretization("a")
|
||||
s = spack.spec.Spec("pkg-a").concretized()
|
||||
|
||||
# Ensure attempt to acquire write lock on the mark raises the exception
|
||||
monkeypatch.setattr(lk.Lock, "acquire_write", _raise_exc)
|
||||
|
||||
spack.store.STORE.failure_tracker.mark(s)
|
||||
|
||||
out = str(capsys.readouterr()[1])
|
||||
assert "Unable to mark a as failed" in out
|
||||
assert "Unable to mark pkg-a as failed" in out
|
||||
|
||||
spack.store.STORE.failure_tracker.clear_all()
|
||||
|
||||
@ -877,7 +878,7 @@ def _raise_exc(lock):
|
||||
def test_prefix_failed(default_mock_concretization, mutable_database, monkeypatch):
|
||||
"""Add coverage to failed operation."""
|
||||
|
||||
s = default_mock_concretization("a")
|
||||
s = spack.spec.Spec("pkg-a").concretized()
|
||||
|
||||
# Confirm the spec is not already marked as failed
|
||||
assert not spack.store.STORE.failure_tracker.has_failed(s)
|
||||
@ -901,7 +902,7 @@ def test_prefix_write_lock_error(default_mock_concretization, mutable_database,
|
||||
def _raise(db, spec):
|
||||
raise lk.LockError("Mock lock error")
|
||||
|
||||
s = default_mock_concretization("a")
|
||||
s = spack.spec.Spec("pkg-a").concretized()
|
||||
|
||||
# Ensure subsequent lock operations fail
|
||||
monkeypatch.setattr(lk.Lock, "acquire_write", _raise)
|
||||
|
@ -30,7 +30,7 @@ def test_true_directives_exist(mock_packages):
|
||||
|
||||
assert cls.dependencies
|
||||
assert spack.spec.Spec() in cls.dependencies["extendee"]
|
||||
assert spack.spec.Spec() in cls.dependencies["b"]
|
||||
assert spack.spec.Spec() in cls.dependencies["pkg-b"]
|
||||
|
||||
assert cls.resources
|
||||
assert spack.spec.Spec() in cls.resources
|
||||
@ -43,7 +43,7 @@ def test_constraints_from_context(mock_packages):
|
||||
pkg_cls = spack.repo.PATH.get_pkg_class("with-constraint-met")
|
||||
|
||||
assert pkg_cls.dependencies
|
||||
assert spack.spec.Spec("@1.0") in pkg_cls.dependencies["b"]
|
||||
assert spack.spec.Spec("@1.0") in pkg_cls.dependencies["pkg-b"]
|
||||
|
||||
assert pkg_cls.conflicts
|
||||
assert (spack.spec.Spec("+foo@1.0"), None) in pkg_cls.conflicts["%gcc"]
|
||||
@ -54,7 +54,7 @@ def test_constraints_from_context_are_merged(mock_packages):
|
||||
pkg_cls = spack.repo.PATH.get_pkg_class("with-constraint-met")
|
||||
|
||||
assert pkg_cls.dependencies
|
||||
assert spack.spec.Spec("@0.14:15 ^b@3.8:4.0") in pkg_cls.dependencies["c"]
|
||||
assert spack.spec.Spec("@0.14:15 ^pkg-b@3.8:4.0") in pkg_cls.dependencies["pkg-c"]
|
||||
|
||||
|
||||
@pytest.mark.regression("27754")
|
||||
@ -68,7 +68,7 @@ def test_extends_spec(config, mock_packages):
|
||||
|
||||
@pytest.mark.regression("34368")
|
||||
def test_error_on_anonymous_dependency(config, mock_packages):
|
||||
pkg = spack.repo.PATH.get_pkg_class("a")
|
||||
pkg = spack.repo.PATH.get_pkg_class("pkg-a")
|
||||
with pytest.raises(spack.directives.DependencyError):
|
||||
spack.directives._depends_on(pkg, "@4.5")
|
||||
|
||||
|
@ -377,10 +377,10 @@ def test_can_add_specs_to_environment_without_specs_attribute(tmp_path, mock_pac
|
||||
"""
|
||||
)
|
||||
env = ev.Environment(tmp_path)
|
||||
env.add("a")
|
||||
env.add("pkg-a")
|
||||
|
||||
assert len(env.user_specs) == 1
|
||||
assert env.manifest.pristine_yaml_content["spack"]["specs"] == ["a"]
|
||||
assert env.manifest.pristine_yaml_content["spack"]["specs"] == ["pkg-a"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -578,7 +578,7 @@ def test_conflicts_with_packages_that_are_not_dependencies(
|
||||
spack:
|
||||
specs:
|
||||
- {spec_str}
|
||||
- b
|
||||
- pkg-b
|
||||
concretizer:
|
||||
unify: true
|
||||
"""
|
||||
@ -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.
|
||||
@ -706,7 +706,7 @@ def test_variant_propagation_with_unify_false(tmp_path, mock_packages):
|
||||
spack:
|
||||
specs:
|
||||
- parent-foo ++foo
|
||||
- c
|
||||
- pkg-c
|
||||
concretizer:
|
||||
unify: false
|
||||
"""
|
||||
@ -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
|
||||
- pkg-c
|
||||
- pkg-c@1.0
|
||||
# Spec used to trigger the bug
|
||||
- pkg-a
|
||||
concretizer:
|
||||
unify: true
|
||||
"""
|
||||
)
|
||||
e = ev.Environment(mutable_mock_env_path)
|
||||
with e:
|
||||
e.concretize()
|
||||
e.deconcretize(spack.spec.Spec("pkg-a"), concrete=False)
|
||||
e.concretize()
|
||||
assert len(e.concrete_roots()) == 3
|
||||
all_root_hashes = {x.dag_hash() for x in e.concrete_roots()}
|
||||
assert len(all_root_hashes) == 2
|
||||
|
@ -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,61 +120,28 @@ 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.not_on_windows("Fails spuriously on Windows")
|
||||
@pytest.mark.disable_clean_stage_check
|
||||
def test_failing_overwrite_install_should_keep_previous_installation(
|
||||
mock_fetch, install_mockery, working_env
|
||||
@ -357,10 +326,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 +611,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())
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user