Compare commits

..

500 Commits

Author SHA1 Message Date
psakievich
c15c483789 Update lib/spack/spack/build_systems/ctest.py
Co-authored-by: Chris White <white238@llnl.gov>
2024-09-17 11:34:52 -06:00
psakievich
a713af9aa8 Update lib/spack/spack/build_systems/ctest.py
Co-authored-by: Chris White <white238@llnl.gov>
2024-09-17 11:34:42 -06:00
psakiev
f7b3d58b73 Some style 2024-09-14 16:30:49 -06:00
psakiev
1d3ae96ae6 CTestBuilder and CTestPackage
This presents a potential new builder/base package target toward
applications using CDash and CTest.
2024-09-14 16:15:59 -06:00
Juan Miguel Carceller
363215717d py-awkward: add v2.6.6 and py-awkward-cpp v35 (#46372)
* py-awkward: add v2.6.6 and py-awkward-cpp v35

* Add dependencies on python and numpy

* Add a conflict with GCC 14

* Move conflict to py-awkward-cpp

* py-awkward: 2.1.1 depends on py-awkward-cpp@12

---------

Co-authored-by: jmcarcell <jmcarcell@users.noreply.github.com>
Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-09-14 15:04:46 -06:00
Alec Scott
ca46fec985 glab: add v1.46.1 (#46353) 2024-09-14 15:47:53 -05:00
Weston Ortiz
7df4ac70da goma: add v7.7.0 (#46362) 2024-09-14 15:45:21 -05:00
Adam J. Stewart
88aa96b53b py-torchmetrics: add v1.4.2 (#46389) 2024-09-14 15:15:29 -05:00
Richard Berger
4da2444a43 py-future: add version 1.0.0 (#46375) 2024-09-14 15:09:49 -05:00
Richard Berger
1a55f2cdab enchant: new versions, update homepage and url (#46386) 2024-09-14 15:05:56 -05:00
Richard Berger
5398b1be15 texlive: add new versions (#46374)
* texlive: use https

* texlive: add 20230313 and 20240312 versions
2024-09-14 13:31:15 -05:00
Jen Herting
a80d2d42d6 py-pydantic-core: new package (#46306)
* py-pydantic-core: new package

* [py-pydantic-core] fixed licence(checked_by)

* [py-pydantic-core] removed unnecessary python dependency

---------

Co-authored-by: Alex C Leute <acl2809@rit.edu>
2024-09-13 13:56:57 -05:00
Jen Herting
60104f916d py-pypdf: new package (#46303)
* [py-pypdf] New package

* [py-pypdf] added webpage
2024-09-13 09:56:47 -05:00
dependabot[bot]
d04358c369 build(deps): bump urllib3 from 2.2.2 to 2.2.3 in /lib/spack/docs (#46368)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.2 to 2.2.3.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.2.2...2.2.3)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-13 08:15:21 -05:00
Kyle Knoepfel
4fe417b620 Optionally output package namespace (#46359) 2024-09-13 08:08:58 -05:00
jeffmauldin
e3c5d5817b Seacas add libcatalyst variant 2 (#46339)
* Update seacas package.py
  Adding libcatalyst variant to seacas package
  When seacas is installed with "seacas +libcatalyst"
  then a dependency on the spack package "libcatalyst"
  (which is catalyst api 2 from kitware) is added, and
  the appropriage cmake variable for the catalyst TPL
  is set. The mpi variant option in catalyst (i.e. build
  with mpi or build without mpi) is passed on to
  libcatalyst. The default of the libcatalyst variant
  is false/off, so if seacas is installed without the
  "+libcatalyst" in the spec it will behave exactly as
  it did before the introduction of this variant.
* shortened line 202 to comply with < 100 characters per line style requirement
2024-09-13 05:36:49 -06:00
Harmen Stoppels
7573ea2ae5 audit: deprecate certain globals (#44895) 2024-09-13 13:21:58 +02:00
Wouter Deconinck
ef35811f39 nasm: add v2.16.03 (#46357)
* nasm: add v2.16.03
* nasm: don't need 2.16.02
2024-09-13 04:40:34 -06:00
Juan Miguel Carceller
a798f40d04 zziplib: add v0.13.78 (#46361)
Co-authored-by: jmcarcell <jmcarcell@users.noreply.github.com>
2024-09-13 04:14:03 -06:00
David Collins
a85afb829e bash: use libc malloc on musl instead of internal malloc (#46370) 2024-09-13 03:54:56 -06:00
Harmen Stoppels
71df434d1f remove self-import cycles (#46371) 2024-09-13 11:16:36 +02:00
Joseph Wang
668aba15a0 py-pycontour: add dev/fixes (#46290) 2024-09-13 09:56:26 +02:00
eugeneswalker
2277052a6b e4s rocm external ci stack: upgrade to rocm v6.2.0 (#46328)
* e4s rocm external ci stack: upgrade to rocm v6.2.0

* magma: add rocm 6.2.0
2024-09-12 17:51:19 -07:00
James Smillie
79d938fb1f py-pip package: fix bootstrap for Python 3.12+ on Windows (#46332) 2024-09-12 14:40:30 -07:00
Wouter Deconinck
6051d56014 fastjet: avoid plugins=all,cxx combinations (#46276)
* fastjet: avoid plugins=all,cxx combinations

* fastjet: depends_on fortran when plugins=all or plugins=pxcone
2024-09-12 15:46:08 -05:00
Wouter Deconinck
94af1d2dfe tl-expected: add v1.1.0; deprecated custom calver version (#46036)
* tl-expected: add v1.1.0; deprecated calver version
* tl-expected: fix url
2024-09-12 12:08:37 -07:00
Jen Herting
d4bc1b8be2 py-httpx: added version 0.27.0 and 0.27.2 (#46311)
* py-httpx: New version
* [py-httpx] fix when for dependencies
* [py-httpx] organized dependencies
* [py-httpx] added version 0.27.2

---------

Co-authored-by: Alex C Leute <aclrc@rit.edu>
2024-09-12 12:00:20 -07:00
Alec Scott
a81baaa12e emacs: add v29.4 (#46350)
* emacs: add v29.4
* confirmed license
* emacs: update git link for https clones

---------

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-09-12 12:48:43 -06:00
Alec Scott
1a17d0b535 go: add v1.23.1 (#46354) 2024-09-12 10:34:43 -07:00
Alec Scott
6d16d7ff83 fd: add v10.2.0 (#46352)
* fd: add v10.2.0
* fd: add rust build dependency constraint
2024-09-12 10:32:33 -07:00
Alec Scott
c89c84770f restic: add v0.17.1 (#46351) 2024-09-12 10:25:04 -07:00
Alec Scott
9154df9062 ripgrep: add v14.1.1 (#46348)
* ripgrep: add v14.1.1
* ripgrep: fix rust dependency type
2024-09-12 10:18:25 -07:00
Alec Scott
56f3ae18f6 kubectl: add v1.31.0 (#46343) 2024-09-12 10:17:16 -07:00
Alec Scott
9a4f83be1d fzf: add v0.55.0 (#46342) 2024-09-12 10:16:15 -07:00
Vanessasaurus
6b10f80cca flux-sched: add v0.37.0, v0.38.0 (#46215)
* Automated deployment to update package flux-sched 2024-09-05
* flux-sched: add back check for run environment
* flux-sched: add conflict for gcc and clang above 0.37.0

---------

Co-authored-by: github-actions <github-actions@users.noreply.github.com>
2024-09-12 10:12:21 -07:00
Lydéric Debusschère
e8e8d59803 cpp-argparse: Add version 3.1 (#46319) 2024-09-12 10:10:23 -07:00
dependabot[bot]
d8d1bc5d7e build(deps): bump pytest from 8.3.2 to 8.3.3 in /lib/spack/docs (#46315)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.2 to 8.3.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.2...8.3.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-12 08:57:05 -07:00
Alec Scott
3531ee28f8 curl: add v8.8.0 (#44622)
Co-authored-by: Harmen Stoppels <harmenstoppels@gmail.com>
2024-09-12 17:05:41 +02:00
Rocco Meli
8b18f95e05 ELSI: add v2.11 and dlaf variant (#46317)
* provide dlaff libs

* fix incompatibility with ntpoly

* add cuda conflicts

* [@spackbot] updating style on behalf of RMeli

* Update var/spack/repos/builtin/packages/elsi/package.py

Co-authored-by: Alberto Invernizzi <9337627+albestro@users.noreply.github.com>

* move pexsi conflict to context

---------

Co-authored-by: RMeli <RMeli@users.noreply.github.com>
Co-authored-by: Alberto Invernizzi <9337627+albestro@users.noreply.github.com>
2024-09-12 06:34:48 -06:00
Xuefeng Ding
128cac34e0 geant4: add hdf5 variant (#46312) 2024-09-12 06:34:26 -06:00
Harmen Stoppels
8fa65b286c fix various pkgs depending on libtool only (#46299)
autotools packages with a configure script should generate the libtool
executable, there's no point in `depends_on("libtool", type="build")`.

the libtool executable in `<libtool prefix>/bin/libtool` is configured
for the wrong toolchain (libtools %compiler instead of the package's
%compiler).

Some package link to `libltdl.so`, which is fine, but had a wrong
dependency type.
2024-09-12 14:05:50 +02:00
Mikael Simberg
6cb16c39ab pika: Add conflicts between pika's and apex's allocator options (#46318) 2024-09-12 13:46:39 +02:00
Wouter Deconinck
7403469413 w3m: add v0.5.3.git20230121 (#45747) 2024-09-12 13:44:55 +02:00
Wouter Deconinck
533de03fef libssh: add v0.9.8, v0.10.6, v0.11.0 (#45689) 2024-09-12 13:41:48 +02:00
Wouter Deconinck
3fea1d6710 codespaces: add ubuntu22.04 (#46100) 2024-09-12 13:40:05 +02:00
Wouter Deconinck
257ebce108 py-zope-*: add new versions (#46338) 2024-09-12 13:39:17 +02:00
Wouter Deconinck
2623b9727f mdspan: fix typo in satisfies condition (#46340) 2024-09-12 13:37:57 +02:00
Wouter Deconinck
68df483bc6 re2: add versions through 2024-07-02; add variant icu (#46337) 2024-09-12 13:37:30 +02:00
Wouter Deconinck
0cc38d685f pcre2: add v10.44 (#46341) 2024-09-12 04:24:49 -06:00
Mikael Simberg
cec770b26e pika: Add conflict between HIP and GCC (libstdc++) >= 13 (#46291) 2024-09-12 09:03:58 +02:00
Todd Gamblin
f417e9f00c acts: further simplify cxxstd handling (#46333)
See https://github.com/spack/spack/pull/46314#discussion_r1752940332.

This further simplifies `cxxstd` variant handling in `acts` by removing superfluous
version constraints from dependencies for `geant4` and `root`.

The version constraints in the loop are redundant with the conditional variant
values here:

```python
    _cxxstd_values = (
        conditional("14", when="@:0.8.1"),
        conditional("17", when="@:35"),
        conditional("20", when="@24:"),
    )
    _cxxstd_common = {
        "values": _cxxstd_values,
        "multi": False,
        "description": "Use the specified C++ standard when building.",
    }
    variant("cxxstd", default="17", when="@:35", **_cxxstd_common)
    variant("cxxstd", default="20", when="@36:", **_cxxstd_common)
```

So we can simplify the dependencies in the loop to:

```python
    for _cxxstd in _cxxstd_values:
        for _v in _cxxstd:
            depends_on(f"geant4 cxxstd={_v.value}", when=f"cxxstd={_v.value} +geant4")
            depends_on(f"geant4 cxxstd={_v.value}", when=f"cxxstd={_v.value} +fatras_geant4")
            depends_on(f"root cxxstd={_v.value}", when=f"cxxstd={_v.value} +tgeo")
```

And avoid the potential for impossible variant expressions.

Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2024-09-11 23:14:50 -06:00
Peter Scheibel
527e0fb4b4 bugfix: unit test broken everywhere by package change(#46344)
Openmpi provider statements were changed in #46102. The package change
was fine in and of itself, but apparently one of our tests depends on
the precise constraints used in those statements. I updated the test
to remove the checks for constraints that were removed.
2024-09-11 19:52:40 -07:00
Stephen Nicholas Swatman
c7e3fc9049 boost: add v1.86.0 (#46321)
This commit adds Boost v1.86.0.
2024-09-11 20:37:43 -06:00
Stephen Nicholas Swatman
5c59bb87a4 acts: add v36.3.0 (#46320)
This commit adds v36.3.0 of ACTS, and also marks a previously missing
conflict with Boost 1.85.0
2024-09-11 16:55:26 -06:00
Stephen Nicholas Swatman
bca975e66a hep tag: new versions as of 2024/09/11 (#46322)
This commit updates acts-algebra-plugins to v0.25.0, actsvg to v0.4.47,
detray to v0.74.2, geomodel to v6.5.0, and vecmem to v1.7.0.
2024-09-11 16:49:18 -06:00
Pranav Sivaraman
8160cd1b9e scnlib: new package (#46313) 2024-09-11 13:18:12 -07:00
Adam J. Stewart
5833fe0c36 py-scikit-learn: add v1.5.2 (#46327) 2024-09-11 12:23:42 -07:00
Adam J. Stewart
ff1bc9e555 py-xgboost: add v2.1.1 (#46260)
* py-xgboost: add v2.1.1
* setuptools no longer needed
* Better comment
2024-09-11 12:00:48 -07:00
Dom Heinzeller
6db1defba0 New packages: py-regionmask and py-pyogrio (#46209)
* Add py-geopandas versions 1.0.0 and 1.0.1, update dependencies
* New package py-pyogrio - dependency of newer py-geopandas
* New package py-regionmask - needs py-geopandas
2024-09-11 11:37:13 -07:00
Todd Gamblin
3099662df2 Fix variant expressions that will never match (#46314)
In #44425, we add stricter variant audits that catch expressions that can never match.

This fixes 13 packages that had this type of issue.

Most packages had lingering spec expressions from before conditional variants with
`when=` statements were added. For example:

* Given `conflicts("+a", when="~b")`, if the package has since added
  `variant("a", when="+b")`, the conflict is no longer needed, because
  `a` and `b` will never exist together.

* Similarly, two packages that depended on `py-torch` depended on
  `py-torch~cuda~cudnn`, which can't match because the `cudnn` variant
  doesn't exist when `cuda` is disabled. Note that neither `+foo` or `~foo`
  match (intentionally) if the `foo` variant doesn't exist.

* Some packages referred to impossible version/variant combinations, e.g.,
  `ceed@1.0.0+mfem~petsc` when the `petsc` variant only exist at version `2`
  or higher.

Some of these correct real issues (e.g. the `py-torch` dependencies would have never
worked). Others simply declutter old code in packages by making all constraints
consistent with version and variant updates.

The only one of these that I think is not all that useful is the one for `acts`,
where looping over `cxxstd` versions and package versions ends up adding some
constraints that are impossible. The additional dependencies could never have
happened and the code is more complicated with the needed extra constriant.
I think *probably* the best thing to do in `acts` is to just not to use a loop
and to write out the constraints explicitly, but maybe the code is easier to
maintain as written.

Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2024-09-11 10:59:16 -07:00
Satish Balay
2185749bb4 openmpi: update dependency wrt mpi standard versions (#46102)
* openmpi: update mpi version dependency
* Use "disjoint sets" for version ranges
2024-09-11 09:13:17 -07:00
Dom Heinzeller
6287d98455 Update FMS: apply patch for fms@2023.03, add variant shared and patch for fms@2024.02 (#46238)
* Update var/spack/repos/builtin/packages/fms/package.py: apply patch for fms@2023.03 to fix compiler options bug in cmake config, add variant shared and corresponding patch for fms@2024.02
* Fix fms package audit: use c9bba516ba.patch?full_index=1 instead of c9bba516ba.patch?full_index=1
* Update checksum of patch for fms@2023.03
2024-09-11 09:08:52 -07:00
Harmen Stoppels
6c4990525d fixesproto: add xextproto dep (#46325)
needed according to pc file
2024-09-11 10:48:56 -05:00
Pierre Augier
5beed521bd py-fluidfft and co: add new packages (#46236)
* Add new packages: py-fluidfft and co

* py-fluidfft: add few contrains (version dependencies)
2024-09-11 09:45:56 -06:00
Adam J. Stewart
5fa8890bd3 CUDA: support Grace Hopper 9.0a compute capability (#45540)
* CUDA: support Grace Hopper 9.0a compute capability

* Fix other packages

* Add type annotations

* Support ancient Python versions

* isort

* spec -> self.spec

Co-authored-by: Andrew W Elble <aweits@rit.edu>

* [@spackbot] updating style on behalf of adamjstewart

---------

Co-authored-by: Andrew W Elble <aweits@rit.edu>
Co-authored-by: adamjstewart <adamjstewart@users.noreply.github.com>
2024-09-11 17:43:20 +02:00
Jannek Squar
122c3c2dbb CDO: add patch for missing algorithm header (#45692)
* add cdo patch for missing algorithm header

* add patch for 2.2.2:2.3.0

Signed-off-by: Jannek <squar@informatik.uni-hamburg.de>

* Add conflict of old cdo with new gcc

* Update var/spack/repos/builtin/packages/cdo/add_algorithm_header_222.patch

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* Adjust patch hash

* Update var/spack/repos/builtin/packages/cdo/package.py

fix copy-paste-error on hash

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

---------

Signed-off-by: Jannek <squar@informatik.uni-hamburg.de>
Co-authored-by: Jannek <squar@informatik.uni-hamburg.de>
Co-authored-by: Try2Code <stark.dreamdetective@gmail.com>
Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-09-11 08:00:56 -05:00
Adam J. Stewart
607d158a44 OpenCV: add v4.8-4.10 (#46223) 2024-09-11 14:52:39 +02:00
Rocco Meli
892b5d8037 cp2k: add v2024.3 (#46297) 2024-09-11 09:58:03 +02:00
Massimiliano Culpo
4f00c7173d nvhpc: add 'compiler' tag to the override (#46301)
fixes #46295

A proper solution would be a tag directive that accumulates tags
with the ones defined in base classes.

For the time being, rewrite them explicitly.

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-09-11 09:47:55 +02:00
James Smillie
fc4a4ec70d boost package: fix Windows build (#43732)
* Boost:Adjust bootstrapping/b2 options as needed for Windows (the
  bootstrapping phase sufficiently differs between Windows/Unix
  that it is handled entirely within its own branch).
* Boost: Paths in user-config.jam should be POSIX, including on Windows
* Python: `.libs` for the Python package should return link libraries
  on Windows. The libraries are also stored in a different directory.
2024-09-11 01:43:28 -06:00
afzpatel
e1da0a7312 Bump up the version for ROCm-6.2.0 (#45701)
* initial update for rocm 6.2
* fix typo in rocprofiler-register
* update rocm-device-libs
* add patch to use clang 18 for roctracer-dev
* add updates for rocm-opencl and rocm-validation-suite
* add hipify-clang patch
* update remaining packages to 6.2
* update hipblaslt mivisionx patches
* update rocm-tensile to 6.2.0
* add hipsparselt changes for 6.2
* add rocm-openmp-extras patch
* add build-time test for rocprofiler-register
* update flang-legacy support for 6.2
* simplify version range
* update boost dependency in rpp

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-09-10 20:13:21 -06:00
Taillefumier Mathieu
f58ee3ea2b SIRIUS: add v7.6.1 (#46296)
bug fix update for sirius
2024-09-10 13:08:32 -06:00
Massimiliano Culpo
ffdfa498bf Deprecate config:install_missing_compilers (#46237)
The option config:install_missing_compilers is currently buggy,
and has been for a while. Remove it, since it won't be needed
when compilers are treated as dependencies.

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-09-10 20:02:05 +02:00
Teague Sterling
decefe0234 perl-bio-ensembl-io: new package (#44509)
* Adding the perl-bio-db-bigfile package

* Adding perl-bio-ensembl-io package

* Update package.py

* Update package.py

* Update package.py

* Update package.py

* Updating dependent package handling

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Updating dependent package handling

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Reverting variants

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Rename package.py to package.py

* Update package.py

* Update package.py

* Removing unneeded dependencies

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Update package.py

* Update package.py

* Update package.py

* Update package.py

* Update package.py

* Updated from testing by @ebiarnie

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Updated from testing by @ebiarnie

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Fixing depends

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Fix styles

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Update package.py

---------

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>
2024-09-10 11:45:59 -06:00
Pranav Sivaraman
e2e5e74b99 fast-float: new package (#46137)
* fast-float: new package

* fast-float: add test dependency

* fast-float: fix doctest dependency type

* fast-float: convert deps to tuple

* fast-float: add v6.1.5 and v6.1.6

* fast-float: patch older versions to use find_package(doctest)
2024-09-10 12:36:23 -05:00
Tony Weaver
0629c5df38 py-your: Changed software pull location (#46201)
* py-your: new package

Spack package recipe for YOUR, Your Unified Reader.  YOUR processes pulsar data in different formats.

Output below from spack install py-your
spack install py-your
==> Installing py-your-0.6.7-djfzsn2lutp24ik6wrk6tjx5f7hil76x [83/83]
==> No binary for py-your-0.6.7-djfzsn2lutp24ik6wrk6tjx5f7hil76x found: installing from source
==> Fetching https://github.com/thepetabyteproject/your/archive/refs/tags/0.6.7.tar.gz
==> No patches needed for py-your
==> py-your: Executing phase: 'install'
==> py-your: Successfully installed py-your-0.6.7-djfzsn2lutp24ik6wrk6tjx5f7hil76x
  Stage: 1.43s.  Install: 0.99s.  Post-install: 0.12s.  Total: 3.12s

* Removed setup_run_environment

After some testing, both spack load and module load for the package will include the bin directory generated by py-your as well as the path to the version of python the package was built with, without the need for the setup_run_environment function.

I removed that function (Although, like Tamara I thought it would be necessary based on other package setups I used as a  basis for this package).

Note: I also updated the required version of py-astropy from py-astropy@4.0: to @py-astropy@6.1.0:  In my test builds, the install was picking up version py-astropy@4.0.1.post1 and numpy1.26.  However when I  tried to run some of the code I was getting errors about py-astropy making numpy calls that are now removed.  The newer version of py-astropy corrects these.  Ideally this would be handled in the py-astropy package to make sure numpy isn't too new

* Changed  software pull location

The original package pulled a tagged release version from GitHub.  That tagged version was created in 2022  and has not been updated since.  It no longer runs because newer versions of numpy have removed deprecation warnings for several of their calls.  The main branch for this repository has addressed these numpy issues as well as some other important fixes but no new release has been generated.  Because of this and the apparent minimal development that now appears to be going on, it is probably best to always pull from the main branch

* [@spackbot] updating style on behalf of aweaver1fandm

* py-your: Changed software pull location

1. Restored original URL and version (0.6.7) as requested
2. Updated py-numpy dependency versions to be constrained based on the version of your.  Because of numpy deprecations related to your version 0.6.7 need to ensure that the numpy version used is not 1.24 or greater because the depracations were removed starting with that version
2024-09-10 10:37:05 -05:00
Robert Underwood
79d778f8cd Add additional cuda-toolkit location variable used by py-torch (#46245)
superceds #46128

Co-authored-by: Robert Underwood <runderwood@anl.gov>
2024-09-10 17:20:05 +02:00
Teague Sterling
6f5ba44431 perl-bioperl: add v1.6.924, v1.7.8, deprecate v1.007002, refactor dependeicies, update url (#46213)
* perl-bioperl: add v1.6.924

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* fix style

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* perl-bioperl: add v1.6.924, v1.7.2, deprecate v1.007002, refactor dependencies

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* perl-bioperl: add v1.7.8

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* perl-bioperl: update url

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* perl-bioperl: cleanup version urls

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* remove v1.7.2

---------

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>
2024-09-10 11:36:43 +01:00
Harmen Stoppels
0905edf592 r: do not create dir in setup_dependent_package (#46282) 2024-09-10 09:04:09 +02:00
Harmen Stoppels
16dba78288 spec.py: dedent format logic (#46279) 2024-09-10 09:02:37 +02:00
Todd Gamblin
b220938d42 bugfix: elfutils has no bzip2 or xz variants (#46294)
Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2024-09-10 06:20:39 +00:00
Mikael Simberg
78117877e0 boost: Refactor header-only install and add missing compiled libraries (#46281) 2024-09-10 08:16:47 +02:00
Harmen Stoppels
975f4fbf84 cmake: remove last occurrences of std_cmake_args globals (#46288) 2024-09-10 07:56:51 +02:00
AcriusWinter
dc853b2cf4 gptune: new test API (#45383)
* gptune: new test API
* gptune: cleanup; finish API changes; separate unrelated test parts
* gptune: standalone test cleanup with timeout constraints
* gptune: ensure stand-alone test bash failures terminate; enable in CI
* gptune: add directory to terminate_bash_failures
* gptune/stand-alone tests: use satisifes for checking variants

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-09-09 15:21:55 -07:00
Philipp Edelmann
69b628a3f0 makedepf90: new package (#45816)
* makedepf90: new package

* reorder versions and dependencies
2024-09-09 16:40:25 -05:00
David Collins
98fb9c23f9 numactl: Add versions 2.0.16-2.0.18 (#46150)
* Add numactl 2.0.16-2.0.18

* Create link-with-latomic-if-needed-v2.0.16.patch

Add a link to libatomic, if needed, for numactl v2.0.16.

* Add some missing patches to v2.0.16

* Create numactl-2.0.18-syscall-NR-ppc64.patch

In short, we need numactl to set __NR_set_mempolicy_home_node on ppc64, if it's not already defined.

* Apply a necessary patch for v2.0.18 on PPC64

* Add libatomic patch for v2.0.16
2024-09-09 14:33:13 -06:00
Pranav Sivaraman
d5e08abe46 py-poxy: add new package (#46214)
* py-poxy: add new package
* py-poxy: depends on misk >= 0.8.1
2024-09-09 12:19:54 -07:00
Harmen Stoppels
2f789f01d3 Revert "Set module variables for all packages before running setup_dependent_…" (#46283)
This reverts commit 6f08db4631.
2024-09-09 10:37:26 -07:00
Kevin Kuriakose
0229240df4 darshan-runtime: fix JOBID determination (#46148) 2024-09-09 10:53:15 -06:00
Harmen Stoppels
9059756a11 reindex: do not assume fixed layout (#46170)
`spack reindex` relies on projections from configuration to locate
installed specs and prefixes. This is problematic because config can
change over time, and we have reasons to do so when turning compilers
into depedencies (removing `{compiler.name}-{compiler.version}` from
projections)

This commit makes reindex recursively search for .spack/ metadirs.
2024-09-09 17:26:30 +02:00
Pierre Augier
67089b967e Add new package py-fluiddyn (#46235) 2024-09-09 09:47:44 -05:00
Mikael Simberg
98e35f7232 pika: Add conflict with fmt 11 and newer (#46280) 2024-09-09 14:30:32 +02:00
Teague Sterling
3ff441c5b0 perl-graphviz: add graphviz dependency (#46268)
Signed-off-by: Teague Sterling <teaguesterling@gmail.com>
2024-09-09 11:08:34 +02:00
Jordan Galby
2502a3c2b6 Fix regression in spec format string for indiviual variants (#46206)
Fix a regression in {variants.X} and {variants.X.value} spec format strings.
2024-09-09 10:42:04 +02:00
Adam J. Stewart
9bdc97043e PyTorch: unpin pybind11 dependency (#46266) 2024-09-09 08:42:09 +02:00
snehring
66ee4caeab nekrs: add v23.0, add new build system (#45992) 2024-09-09 07:32:14 +02:00
Eric Berquist
18218d732a intel-pin: add up to v3.31 (#46185) 2024-09-09 07:28:53 +02:00
Paul Ferrell
4c6b3ccb40 charliecloud: fix libsquashfuse dependency type (#46189)
Should have been a link depenedency, not a build dependency.
2024-09-09 07:26:20 +02:00
Satish Balay
1d9c8a9034 xsdk: add amrex variant (#46190)
and remove compiler conditionals [as amrex conflict with clang no longer exists since #22967]
2024-09-09 07:19:31 +02:00
AMD Toolchain Support
216619bb53 namd: variant updates (#45825)
* Add missing variant, already used in recipe (avxtiles)

* Add memopt variant 

Co-authored-by: viveshar <vivek.sharma2@amd.com>
2024-09-09 07:12:16 +02:00
Wouter Deconinck
47c771f03f assimp: add v5.4.3, enable testing (#46267) 2024-09-09 06:51:57 +02:00
Wouter Deconinck
c7139eb690 wayland-protocols: add v1.37 (#46269) 2024-09-09 06:50:30 +02:00
Wouter Deconinck
74ba81368a py-vector: add v1.5.1 (#46271) 2024-09-09 06:49:35 +02:00
Wouter Deconinck
ef11fcdf34 protobuf: patch @3.22:3.24.3 when ^abseil-cpp@20240116: (#46273) 2024-09-09 06:48:50 +02:00
Wouter Deconinck
c4f3348af1 (py-)onnx: add v1.16.2; onnx: enable testing (#46274) 2024-09-09 06:47:45 +02:00
Richard Berger
fec2f30d5a lammps: add 20240829 and 20230802.4 releases (#46131) 2024-09-09 06:46:12 +02:00
Wouter Deconinck
6af92f59ec xrootd: add v5.7.1 (#46270) 2024-09-09 06:45:15 +02:00
Pierre Augier
cb47c5f0ac Add package py-transonic (#46234) 2024-09-08 20:03:09 -05:00
Dom Heinzeller
32727087f1 Add patch vfile_cassert.patch for ecflow@5.11.4 (#46095) 2024-09-07 07:54:43 -06:00
Jen Herting
5f9c6299d1 [py-langsmith] added version 0.1.81 (#46259) 2024-09-07 04:29:45 -06:00
Jen Herting
541e40e252 py-httpcore: add v1.0.5 (#46123)
* py-httpcore: Added new version

* [py-httpcore]

- added version 0.18.0
- restructured dependencies as everything has a when and
  type/when ordering was all over the place

* [py-httpcore] ordered dependencies in the order listed in v1.0.5 pyproject.toml

---------

Co-authored-by: Alex C Leute <aclrc@rit.edu>
2024-09-07 02:28:15 -06:00
Jen Herting
ddc8790896 [py-lpips] new package (#46256) 2024-09-06 18:54:51 -06:00
Jen Herting
7d6b643b58 [py-torch-fidelity] New package (#46257)
* [py-torch-fidelity] New package
* [py-torch-fidelity] add patch to fix missing requirements.txt
2024-09-06 18:54:36 -06:00
Dan Lipsa
6f08db4631 Set module variables for all packages before running setup_dependent_package (#44327)
When a package is running `setup_dependent_package` on a parent, ensure
that module variables like `spack_cc` are available. This was often
true prior to this commit, but externals were an exception.

---------

Co-authored-by: John Parent <john.parent@kitware.com>
2024-09-06 18:54:09 -06:00
eugeneswalker
7086d6f1ac use updated container with cmake@3.30.2 (#46262) 2024-09-06 18:48:45 -06:00
Robert Underwood
7a313295ac Sz3 fix (#46263)
* Updated version of sz3
  Supercedes #46128
* Add Robertu94 to maintainers fo r SZ3

---------

Co-authored-by: Robert Underwood <runderwood@anl.gov>
2024-09-06 14:35:55 -07:00
Jen Herting
3bdaaaf47c New package: py-monai (#46140)
* New package: py-monai

* Fixed linked issues with py-monai

* [py-monai] removed extra line

* [py-monai]

- New version 1.3.2
- ran black
- update copyright

* [py-monai] added license

* [py-monai]

- moved build only dependencies
- specified newer python requirements for newer versions

---------

Co-authored-by: vehrc <vehrc@rit.edu>
2024-09-06 15:19:12 -06:00
Massimiliano Culpo
a3fa54812f Remove deprecated config options (#44061)
These options have been deprecated in v0.21, and
slated for removal in v0.23
2024-09-06 15:14:36 -06:00
Kyle Gerheiser
afc9615abf libfabric: Add versions v1.22.0 and v1.21.1 (#46188)
* Add libfabric v1.22.0 and v1.21.1

* Fix whitespace
2024-09-06 14:21:41 -05:00
Adam J. Stewart
19352af10b GEOS: add v3.13.0 (#46261) 2024-09-06 12:17:41 -07:00
Robert Underwood
e4f8cff286 Updated version of sz3 (#46246)
Supercedes #46128

Co-authored-by: Robert Underwood <runderwood@anl.gov>
2024-09-06 12:15:03 -07:00
Sam Reeve
76df9de26a Update ExaCA backend handling and add version 2.0 (#46243)
* Add exaca 2.0
* Add kokkos/cuda/hip backend support for exaca
2024-09-06 14:53:21 -04:00
Auriane R.
983e7427f7 Replace if ... in spec with spec.satisfies in f* and g* packages (#46127)
* Replace if ... in spec with spec.satisfies in f* and g* packages

* gromacs: ^amdfftw -> ^[virtuals=fftw-api] amdfftw

* flamemaster: add virtuals lapack for the amdlibflame dependency

---------

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-09-06 13:22:19 -05:00
Adam J. Stewart
d9df520e85 py-pandas: relax build dependencies (#46229) 2024-09-06 11:19:49 -07:00
Adam J. Stewart
b51fd9f5a6 py-torchgeo: zipfile-deflate64 no longer required (#46233) 2024-09-06 11:18:10 -07:00
Robert Underwood
8058cd34e4 Require a newer version of cudnn for cupy (#46248)
cupy 12 needs a newer version of cudnn as documented here.  Supercedes #46128

Co-authored-by: Robert Underwood <runderwood@anl.gov>
2024-09-06 10:56:46 -07:00
Robert Underwood
2f488b9329 Use an HTTP git url for libpressio-opt (#46249)
superceds #46128

Co-authored-by: Robert Underwood <runderwood@anl.gov>
2024-09-06 10:43:23 -07:00
Peter Scheibel
78a4d3e7d2 Mixed-source cflags (#41049)
Allow flags from different sources (compilers, `require:`, command-line
specs, and `depends_on`) to be merged together, and enforce a consistent
order among them.

The order is based on the sources, e.g. flags on specs from the command
line always come last. Some flag order consistency issues are fixed:

1. Flags from `compilers.yaml` and the command line were always intra- and
   inter-source order consistent.
2. Flags from dependents and packages.yaml (introduced via `require:`)
   were not: for `-a -b` from one source and `-c` from another, the final
   result might rearrange `-a -b`, and would also be inconsistent in terms
   of whether `-c` came before or after.

(1) is/was handled by going back to the original source, i.e., flags are
retrieved directly from the command line spec rather than the solver.

(2) is addressed by:

* Keeping track of grouped flags in the solver
* Keeping track of flag sources in the solver on a per-flag basis

The latter info is used in this PR to enforce DAG ordering on flags
applied from multiple dependents to the same package, e.g., for this
graph:

```
   a
  /|\
 b | c
  \|/
   d
```

If `a`, `b`, and `c` impose flags on `d`, the combined flags on `d` will
contain the flags of `a`, `b`, and `c` -- in that order. 

Conflicting flags are allowed (e.g. -O2 and -O3). `Spec.satisifes()` has
been updated such that X satisfies Y as long as X has *at least* all of
the flags that Y has. This is also true in the solver constraints.
`.satisfies` does not account for how order can change behavior (so
`-O2 -O3` can satisfy `-O3 -O2`); it is expected that this can be
addressed later (e.g. by prohibiting flag conflicts).

`Spec.constrain` and `.intersects` have been updated to be consistent
with this new definition of `.satisfies`.
2024-09-06 10:37:33 -07:00
Juan Miguel Carceller
2b9a621d19 sleef: add the PIC flag (#46217) 2024-09-06 09:37:20 -06:00
Juan Miguel Carceller
fa5f4f1cab pthreadpool: add the PIC flag (#46216) 2024-09-06 15:11:03 +02:00
John W. Parent
4042afaa99 Bootstrap GnuPG and file on Windows (#41810)
Spack can now bootstrap two new dependencies on Windows: GnuPG, and file. 

These dependencies are modeled as a separate package, and they install a cross-compiled binary.
Details on how they binaries are built are in https://github.com/spack/windows-bootstrap-resources
2024-09-06 14:26:46 +02:00
Harmen Stoppels
7fdf1029b7 fish: use shlex.quote instead of custom quote (#46251) 2024-09-06 11:38:14 +00:00
Stephen Nicholas Swatman
814f4f20c0 py-pybind11: add v2.13.0-v2.13.4 and new conflict (#45872)
This PR adds py-pybind11 versions 2.13.0, 2.13.1, 2.13.2, 2.13.3, and
2.13.4. It also adds a new conflict between gcc 14 and pybind versions
up to and including 2.13.1.
2024-09-06 10:38:35 +02:00
Harmen Stoppels
7c473937ba db: more type hints (#46242) 2024-09-06 09:13:14 +02:00
Robert Underwood
9d754c127a Require a newer version of cudnn for cupy (#46247)
cupy 12 needs a newer version of cudnn as documented here.  Supercedes #46128

Co-authored-by: Robert Underwood <runderwood@anl.gov>
2024-09-05 23:12:46 -06:00
Massimiliano Culpo
9a8bff01ad Allow deprecating more than one property in config (#46221)
* Allow deprecating more than one property in config

This internal change allows the customization of errors
and warnings to be printed when deprecating a property.

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>

* fix

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>

* Use a list comprehension for "issues"

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>

---------

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-09-05 15:33:20 -07:00
Nicole C.
434a703bcf Windows: Update pytest with echo and remove others (#45620)
* Windows: Update pytest with echo and remove others

* Fix style

* Remove unused pytest import in undevelop

* Update test_test_part_pass to be Window compatible

* Style
2024-09-05 15:34:19 -05:00
Adam J. Stewart
a42108438d py-contourpy: add v1.3.0 (#46225) 2024-09-05 22:16:14 +02:00
Sebastian Pipping
1be9b7f53c expat: Add 2.6.3 with security fixes + deprecate vulnerable 2.6.2 (#46208) 2024-09-05 14:09:34 -06:00
Dom Heinzeller
6b05a80745 Bug fixes for building py-netcdf4 and py-ruamel-yaml-clib with apple-clang@15 (#46184)
* Fix compiler flags in py-netcdf4 for apple-clang@15:
* Fix compiler flags in py-ruamel-yaml-clib for apple-clang@15:
2024-09-05 11:29:26 -07:00
Fernando Ayats
4d36b0a5ef npb: fix mpi rank mismatch errors (#46075)
MPI variant has several rank mismatch errors, which are silently
ignored. This downgrades the errors to warnings.
2024-09-05 11:27:15 -07:00
Adam J. Stewart
636843f330 PyTorch: update ecosystem (#46220) 2024-09-05 11:06:57 -07:00
Adam J. Stewart
d4378b6e09 awscli-v2: remove upperbound on cryptography (#46222) 2024-09-05 11:04:56 -07:00
Adam J. Stewart
69b54d9039 py-imageio: add v2.35.1 (#46227) 2024-09-05 10:57:57 -07:00
Adam J. Stewart
618866b35c py-laspy: add v2.3.5 (#46228) 2024-09-05 10:56:51 -07:00
Adam J. Stewart
47bd8a6b26 py-pyvista: add v0.44.1 (#46231) 2024-09-05 10:53:42 -07:00
Adam J. Stewart
93d31225db py-tifffile: add v2024 (#46232) 2024-09-05 10:52:21 -07:00
Alex Richert
c3a1d1f91e Add when's to some tau dependencies (#46212)
* Add when's to some tau dependencies

* [@spackbot] updating style on behalf of AlexanderRichert-NOAA
2024-09-05 10:35:01 -07:00
estewart08
33621a9860 [rocm-openmp-extras] - Add support for flang-legacy in 6.1.2 (#46130)
* [rocm-openmp-extras] - Add support for flang-legacy in 6.1.2
* [rocm-openmp-extras] - Remove unused variable flang_legacy_dir
* [rocm-openmp-extras] - Limit flang-legacy build to 6.1 and newer ROCm versions
2024-09-05 12:27:50 -05:00
etiennemlb
055eb3cd94 PLUMED: Using a C compiler variable (#46082)
* Using a C compiler variable

* homogeneity
2024-09-05 10:43:28 -06:00
Massimiliano Culpo
c4d18671fe Avoid best-effort expansion of stacks (#40792)
fixes #40791

Currently stacks behave differently if used in unify:false
environments, which leads to inconsistencies during concretization.

For instance, we might have two abstract user specs that do not
intersect with each other map to the same concrete spec in the
environment. This is clearly wrong.

This PR removes the best effort expansion, so that user specs
are always applied strictly.
2024-09-05 10:32:15 -06:00
Thomas Madlener
5d9f0cf44e gaudi: Specify boost dependencies explicitly and cleanup package (#46194)
* gaudi: Specify boost components and add +fiber for v39

* gaudi: Limit fmt version to allow building master branch

* Make boost dependencies a bit more readable

* Remove patches for no longer existing versions
2024-09-05 08:27:04 -06:00
Harmen Stoppels
02faa7b97e reindex: ensure database is empty before reindex (#46199)
fixes two tests that did not clear the in-memory bits of a database
before calling reindex.
2024-09-05 14:53:29 +02:00
Mikael Simberg
d37749cedd pika: Add 0.28.0 (#46207)
* pika: Add 0.28.0

* Add conflict between pika 0.28.0 and dla-future
2024-09-05 14:12:21 +02:00
Harmen Stoppels
7e5e6f2833 Pass Database layout in constructor (#46219)
Ensures that Database instances do not reference a global
`spack.store.STORE.layout`. Simplify Database.{add,reindex} signature.
2024-09-05 10:49:03 +00:00
Massimiliano Culpo
37ea9657cf Remove test_external_package_module (#46218)
This test was possibly meant for the Cray platform, and
currently is a no-op.

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-09-05 12:10:59 +02:00
Harmen Stoppels
2107a88514 spack deprecate: deprecate --link-type flag (#46202) 2024-09-05 11:06:46 +02:00
Auriane R.
1a4b07e730 Replace if ... in spec with spec.satisfies in d* and e* packages (#46126)
* Replace if ... in spec with spec.satisfies in d* and e* packages

* Use virtuals for different mpi implementations in esmf

* esmf: ^[virtuals=mpi] mpt

* extrae: ^[virtuals=mpi] intel-oneapi-mpi

---------

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-09-04 22:33:51 -06:00
Jack Morrison
c98045e028 libfabric: Add CUDA variant (#46203)
Signed-off-by: Jack Morrison <jack.morrison@cornelisnetworks.com>
2024-09-04 21:43:25 -06:00
mvlopri
bc5456a791 seacas: require +metis and +mpi instead of +parmetis (#46205)
This change aligns the build condition for parmetis with the
depends_on condition.
The current build condition of parmetis looks for "+parmetis" in
the spec which is not added by the depends_on as that adds
"^parmetis" instead.
2024-09-04 21:03:02 -06:00
Pranav Sivaraman
656720a387 py-schema: add v0.7.7 (#46210)
* py-schema: add v0.7.7

* py-schema: fix when spec

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

---------

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-09-04 20:07:35 -06:00
Mikael Simberg
9604c0a9b3 boost: Conditionally include/exclude Boost.Json depending on Boost version (#46200) 2024-09-04 16:50:06 -06:00
Pranav Sivaraman
ee96194486 py-misk: add new package (#46153) 2024-09-04 11:25:01 -07:00
Felix Thaler
ab21fc1daf Added jump package (#46164) 2024-09-04 11:16:20 -07:00
Pranav Sivaraman
d593ad0c06 py-trieregex: new package (#46154)
* py-trieregex: new package
2024-09-04 10:58:08 -07:00
Weiqun Zhang
254fe6ed6e amrex: add v24.09 (#46171) 2024-09-04 10:54:49 -07:00
renjithravindrankannath
7e20874f54 rocm-openmp-extras: Avoiding registration of duplicate check-targets and fix for the failure in hostexec (#45658)
* Adding addtional check for omptarget library for amdgpu in nvidia environment
* Avoiding registration of duplicate when built on cuda
* Adding hsa library path in LD_LIBRARY_PATH
* Correction in hsa prefix library path in LD_LIBRARY_PATH
2024-09-04 10:31:28 -07:00
Vanessasaurus
cd4c40fdbd Automated deployment to update package flux-core 2024-09-04 (#46191)
Co-authored-by: github-actions <github-actions@users.noreply.github.com>
2024-09-04 10:28:44 -07:00
Harmen Stoppels
c13e8e49fe goaccess: new package (#46193) 2024-09-04 10:23:28 -07:00
Adam J. Stewart
35a2a0b3d0 py-rasterio: add v1.3.11 (#46195)
* py-rasterio: add v1.3.11
* Use default_args
2024-09-04 09:45:16 -07:00
Adam J. Stewart
22d69724f4 py-fiona: add v1.10.0 (#46196) 2024-09-04 09:36:43 -07:00
Adam J. Stewart
f6e3f6eec7 py-numpy: add v2.1.1 (#46197) 2024-09-04 09:33:07 -07:00
G-Ragghianti
866c440f0c Updating git repo location (#46183) 2024-09-04 10:34:05 -05:00
Juan Miguel Carceller
fe6f5b87dc texlive: clean up recipe (#45863)
* texlive: clean up recipe

* Update the poppler dependency

* Fix typo

---------

Co-authored-by: jmcarcell <jmcarcell@users.noreply.github.com>
2024-09-04 10:20:23 -05:00
Vicente Bolea
3df3f40984 paraview: add new v5.13.0 release (#46091) 2024-09-04 10:14:52 -05:00
Satish Balay
33f4a40df4 trilinos: update @[master,develop] dependency on kokkos (#46182)
==> Error: InstallError: For Trilinos@[master,develop], ^kokkos version in spec must match version in Trilinos source code. Specify ^kokkos@4.4.00 for trilinos@[master,develop] instead of ^kokkos@4.3.01.
2024-09-04 07:34:20 -06:00
Kyle Knoepfel
7ba0132f66 llvm: improve detection regexes (#46187) 2024-09-04 04:12:42 -06:00
Mikael Simberg
744f034dfb Add conflict between llvm-amdgpu until version 5 and ninja since version 1.12.0 (#45788)
* Add conflict between llvm-amdgpu and ninja since version 1.12.0

* Update llvm-amdgpu and ninja conflict to extend to 6.0
2024-09-04 10:37:48 +02:00
Massimiliano Culpo
d41fb3d542 llvm: be more strict with detection (#46179) 2024-09-03 22:37:30 +02:00
Laurent Aphecetche
9b077a360e root: fix @loader_path on macOS (#44826)
* root: fix @loader_path on macOS

* root: use loader patch from upstream

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* add missing comma

* root: rm unused patch

* root: conflict on macos 12 for @:6.27 when +python

---------

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-09-03 09:50:31 -05:00
Stephen Nicholas Swatman
5c297d8322 root: fix X11 and OpenGL-related issues on macOS (#45632)
* root: Add dependency on libglx

We have been trying to build the Acts package on MacOS, and in this
process we have been running into problems with the ROOT spec on that
operating system; the primary issue we are encountering is that the
compiler is unable to find the `GL/glx.h` header, which is part of glx.
It seems, therefore, that ROOT depends on libglx, but this is not
currently encoded in the spec. This commit ensures that ROOT depends on
the virtual libglx package when both the OpenCL and X11 variants are
enabled.

* Enable builtin glew on MacOS

* Allow `root+opengl+aqua~x` on macOS
2024-09-03 08:32:17 -05:00
Massimiliano Culpo
9e18e63053 solver: minor cleanup and optimization (#46176)
Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-09-03 10:35:54 +00:00
Harmen Stoppels
069286bda7 database: remove a few class properties (#46175) 2024-09-03 11:22:14 +02:00
Richard Berger
1679b5e141 nvpl updates (#45922)
* nvpl-blas: add new version 0.3.0

* nvpl-lapack: add new version 0.2.3.1
2024-09-03 08:07:24 +02:00
Pierre Augier
1f935ac356 Add py-pytest-allclose package (#45877) 2024-09-02 14:34:24 -06:00
Pariksheet Nanda
e66e572656 py-cellprofiler: add 4.2.6 new package (#44824)
* py-cellprofiler: add 4.2.6 new package

* py-mysqlclient: Limit pkg-config patch to @1.4:

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

---------

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-09-02 14:21:39 -05:00
JCGoran
17d3d17d46 py-sympy: add version 1.13.0 (#46163)
* sympy: add version 1.13.0

* sympy: update deps
2024-09-02 09:51:44 -06:00
Thomas Madlener
c814fb5fe6 podio: Add the new datasource variant once it is available (#46078)
* podio: Add the new datasource variant once it is available

* Make sure to require a suitable minimal root version
2024-09-02 08:39:43 -05:00
Harmen Stoppels
fe2d06399f db: type hints (#46168) 2024-09-02 14:44:49 +02:00
Harmen Stoppels
b5dec35113 projections: simplify expression (#46167) 2024-09-02 12:38:47 +00:00
Harmen Stoppels
20565ba8ab remove dead code: fs.is_writable_dir was used on file (#46166) 2024-09-02 11:55:40 +00:00
Wouter Deconinck
c47a3ee05b package_base: sort deprecated versions later in preferred_version (#46025) 2024-09-02 10:42:15 +00:00
Wouter Deconinck
aaa7469b92 py-vector: add v1.4.2, v1.5.0; variant awkward (#46039) 2024-09-02 09:41:15 +02:00
Asher Mancinelli
1f8a6d8e8b [neovim] add utf8proc dependency (#46064)
I believe utf8proc was added as a neovim dependency in neovim/neovim#26165
and is only in the master branch.
2024-09-02 09:28:30 +02:00
Richard Berger
654bf45c01 kokkos-nvcc-wrapper: add new 4.4.00 version (#46067) 2024-09-02 09:27:13 +02:00
Wouter Deconinck
daa42be47f ddt: deprecate all versions in favor of linaro-forge (#46115) 2024-09-02 09:15:47 +02:00
Satish Balay
ca179deb8e petsc, py-petsc4py: add v3.21.5 (#46151) 2024-09-02 09:09:05 +02:00
Satish Balay
8f6092bf83 xsdk: remove develop and 0.7.0, and deprecate 0.8.0 (#46121) 2024-09-02 09:04:36 +02:00
pauleonix
6b649ccf4f cuda: add v12.6.1 (#46143)
Update build system conflict between CUDA 12.6 and Clang 18
2024-09-02 08:46:03 +02:00
Georgia Stuart
d463d4566d docs: update conditional definition arch (#46139)
Signed-off-by: Georgia Stuart <gstuart@umass.edu>

Co-authored-by: Jordan Galby <67924449+Jordan474@users.noreply.github.com>
2024-09-02 08:32:26 +02:00
Adam J. Stewart
f79be3022b py-torchgeo: add v0.6.0 (#46158) 2024-09-02 08:30:02 +02:00
eugeneswalker
7327e731b4 e4s ci stacks: add geopm-runtime (#45881) 2024-09-02 08:24:10 +02:00
Adam J. Stewart
ab6d494fe4 py-horovod: py-torch 2.1.0 now supported (#46152) 2024-09-02 08:21:16 +02:00
Christophe Prud'homme
e5aa74e7cb package cln 1.3.7 feelpp/spack#2 (#46162)
* package cln 1.3.7 feelpp/spack#2

* add myself as maintainer

* fix style issue, rm blankline
2024-09-01 22:38:26 -05:00
Gavin John
728f8e2654 nanomath: add version 1.4.0 (#46159) 2024-09-01 17:23:57 -06:00
Stephen Sachs
9a58a6da0d [openmpi] Add optional debug build variant (#45708) 2024-09-01 11:02:04 -05:00
Stephen Nicholas Swatman
395491815a dd4hep: mark conflict with root@6.31.1: (#45855)
dd4hep versions up to and including 1.27 had a conflict with root
versions starting from 6.31.1, as shown in
https://github.com/AIDASoft/DD4hep/issues/1210. This PR explicitly adds
that conflict to the spec.
2024-09-01 10:54:20 -05:00
Joseph Wang
fd98ebed9d add rapidjson conflict for gcc14 (#46007) 2024-09-01 10:01:53 -05:00
Harmen Stoppels
f7de621d0c Remove redundant inspect.getmodule(self) idiom in packages (#46073) 2024-09-01 11:25:51 +02:00
dunatotatos
a5f404cff5 Update py-numcodecs. (#45715) 2024-08-31 15:15:18 -06:00
Martin Pokorny
8100b0d575 casacore: add new versions 3.6.1, 3.6.0, 3.2.1 (#46068) 2024-08-31 15:14:52 -06:00
Juan Miguel Carceller
b38ab54028 whizard: add a patch when using hepmc3 3.3.0 or newer (#45862)
* whizard: add a patch when using hepmc3 3.3.0 or newer

* whizard: comment with patch origin

---------

Co-authored-by: jmcarcell <jmcarcell@users.noreply.github.com>
Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-08-31 13:13:42 -06:00
Stephen Nicholas Swatman
412f22b76a podio: apply patch for gcc 14 builds (#45854)
* podio: apply patch for gcc 14 builds

Podio versions after 0.17.0 but before 1.0.0 are broken when using gcc
14 because of a missing include, which is addressed in the podio pull
request at https://github.com/AIDASoft/podio/pull/600. This commit
patches pre-1.0.0 versions of Podio so they can be compiled with gcc 14,
which is important for building Acts.

* Style

* Style 2

* Fixes

* Add comment:

* Add sha256
2024-08-31 13:42:02 -05:00
Jen Herting
d226ef31bd New package: py-jsonlines (#46124)
* py-jsonlines: new package

* py-jsonlines: fix dependency

---------

Co-authored-by: Alex C Leute <acl2809@rit.edu>
2024-08-31 12:30:07 -05:00
Jen Herting
ae32af927d New package: py-ops (#46122)
* New package: py-ops

* [py-ops]

- added version 2.16.0
- ran black
- updated copyright
- added license()

---------

Co-authored-by: vehrc <vehrc@rit.edu>
2024-08-31 12:11:27 -05:00
Alex Richert
400dd40492 sigio: add v2.3.3 (#46116) 2024-08-31 12:01:08 -05:00
dependabot[bot]
04bdff33ad build(deps): bump actions/setup-python from 5.1.1 to 5.2.0 (#46129)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.1 to 5.2.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](39cd14951b...f677139bbe)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-31 11:51:49 -05:00
Pranav Sivaraman
017e3dd417 doctest: add new package (#46138) 2024-08-31 11:48:20 -05:00
Alex Richert
f7e3902ca8 landsfcutil: add v2.4.2 (#46144) 2024-08-31 09:13:10 -05:00
Alex Richert
89da8d4c84 gfsio: add v1.4.2 (#46145) 2024-08-31 09:12:23 -05:00
Alex Richert
8cac74699b sfcio: add v1.4.2 (#46146)
* sfcio: add v1.4.2

* [@spackbot] updating style on behalf of AlexanderRichert-NOAA
2024-08-31 09:11:13 -05:00
dependabot[bot]
db311eef46 build(deps): bump actions/upload-artifact from 4.3.6 to 4.4.0 (#46149)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.6 to 4.4.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](834a144ee9...50769540e7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-31 08:38:16 -05:00
etiennemlb
1427735876 unzip: use more generic strip flag for cce (#46087)
* Use more generic strip flag for cce

* [@spackbot] updating style on behalf of etiennemlb

* Apply always
2024-08-30 12:57:24 -05:00
Kacper Kornet
f88ca8cc1f plumed: add v2.9.1 (#46022) 2024-08-30 15:29:49 +02:00
Massimiliano Culpo
bf1f4e15ee boost: remove Compiler.cxx_names (#46037) 2024-08-30 13:25:40 +02:00
Harmen Stoppels
dd756d53de windows-vis: fix failing pipeline (#46135)
* seacas: fix gnu parallel dep

* add vtk@9.0 platform=windows conflict
2024-08-30 12:57:16 +02:00
Massimiliano Culpo
1c1970e727 Put some more constraint on a few mpi providers (#46132)
This should help not selecting, by default, some niche implementation that are supposed to be externals.

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-08-30 11:16:35 +02:00
Massimiliano Culpo
c283fce487 Remove DetectedPackage class (#46071)
This PR simplifies the code doing external spec detection by removing 
the `DetectedPackage` class. Now, functions accepting or returning lists 
of `DetectedPackage`, will accept or return list of specs.

Performance doesn't seem to change if we use `Spec.__reduce__` instead 
of `DetectionPackage.__reduce__`.
2024-08-30 08:11:17 +00:00
Harmen Stoppels
199cbce5ef windows-vis: vtk~mpi (#46133) 2024-08-30 09:38:41 +02:00
Massimiliano Culpo
82dea7e6ce mpitrampoline: fix wrong use of compiler.cc_names (#46038) 2024-08-30 07:50:09 +02:00
Harmen Stoppels
bd71ce5856 cray: allow failure due to broken blas (#46111)
1. libsci_cray.so is broken, as it fails to list `libdl.so` in
   DT_NEEDED.
2. cray compilers fail to build a different blas
2024-08-30 07:49:10 +02:00
Mark Abraham
73fc86cbc3 gromacs: support version 2024.3 (#46117) 2024-08-29 17:38:04 -06:00
Chris White
3589edcc6d conduit package: Honor compiler extra_rpaths + extras (#46046)
- Honor compiler extra_rpaths (this build bypasses spack wrappers,
  so the RPATHs are added explicitly as CMake options)
- Use root_cmakelists_dir instead of adding it to the command line
- Add BLT as a dependency, allowing different versions outside of what
  is in the tarball
- Put a copy of host-config in the stage directory: this allows
  examination of the host-config when a build fails (before, the
  host-config was just stored in the install directory, which is
  deleted by default on a failed build)
2024-08-29 13:59:15 -07:00
Chris Marsh
cade66d842 CMakePackage: Set policy CMP0042 NEW on macos (#46114)
so linking to shared libraries works well at runtime on darwin for all packages with cmake_minimum_required < 3.
2024-08-29 18:49:53 +00:00
Louise Spellacy
c5766aa757 linaro-forge: added 24.0.4 version (#46112)
Updated platform.machine() to only match "aarch64".
"arm64" not supported by "spack arch"
2024-08-29 12:33:49 -06:00
Harmen Stoppels
c3e9bd4fbf spectrum-mpi: no windows (#46119) 2024-08-29 20:04:33 +02:00
Harmen Stoppels
05357052ac py-greenlet: add missing forward compat bound (#46113) 2024-08-29 17:55:26 +02:00
Harmen Stoppels
dc3f5cd606 windows_establish_runtime_linkage: post install hook (#46103) 2024-08-29 17:16:36 +02:00
Alberto Invernizzi
9a16927993 paraview: add cdi support (#44222)
* add basic CDI package

* add CDI variant to paraview

* [@spackbot] updating style on behalf of albestro

---------

Co-authored-by: albestro <albestro@users.noreply.github.com>
2024-08-29 14:19:04 +02:00
Wouter Deconinck
093b273f5c py-mypy: add v1.11.2 (#46099) 2024-08-29 05:46:36 -06:00
Tamara Dahlgren
b6ff126494 Executable: make the timeout message readable (#46098) 2024-08-29 11:46:15 +00:00
Harmen Stoppels
fe8f631b7d tau: fix (cray) compiler names/paths (#46104)
fixes a build issue on cray ci
2024-08-29 11:24:58 +02:00
Massimiliano Culpo
f9065f0c7e Remove "get_compiler_duplicates" (#46077)
This function is used only in tests.
2024-08-29 06:53:17 +02:00
etiennemlb
699735016f Add more compiler leniency (#46083) 2024-08-28 16:33:26 -07:00
Arne Becker
ec85bbe3f7 perl-compress-bzip2: new package (#46055)
* perl-compress-bzip2: new package
  Adds Compress::Bzip2
* Use bzip2 from Spack, not system
2024-08-28 13:57:01 -07:00
Taillefumier Mathieu
7e1ad8f321 [Update] New version of sirius (#46049) 2024-08-28 13:38:06 -07:00
Derek Ryan Strong
0eb8f4f0f9 pmix: add v5.0.3 and fix variants (#45621)
* Add pmix v5.0.3 and fix variants
* Update pmix homepage link
* pmix: Simplify/update hwloc dependency
* pmix: Update versions for --disable-sphinx configure option
* pmix: Add munge variant
* pmix: Add zlib dependency
* pmix: Fix dependency py-sphinx@5
2024-08-28 12:59:48 -07:00
renjithravindrankannath
ee27dc5d45 llvm-amdgpu: Updating LD_LIBRARY_PATH w.r.t new prefix path (#45940)
* Updating LD_LIBRARY_PATH w.r.t new prefix path

* Updating hsa external path for 6.x
2024-08-28 11:26:05 -07:00
Robert Underwood
ec0a57cba4 py-numcodecs rename git branch to match upstream (#46085)
* py-numcodecs rename git branch to match upstream
2024-08-28 19:16:23 +02:00
Jordan Galby
4c91e6245c Don't check checksums on spack-develop packages (#46076)
Fix regression introduced in spack 0.22.1 where Spack would ask about checksums
on spack-develop packages.
2024-08-28 17:07:30 +00:00
Satish Balay
6a1dfbda97 mfem, pflotran, alquimia: remove old versions with xsdk string (in version) that were used in old/removed xsdk releases (#45837) 2024-08-28 10:03:51 -07:00
Jordan Galby
6b0011c8f1 For "when:" and install_environment.json: Support fully qualified hostname (#45522) 2024-08-28 18:38:54 +02:00
Nicholas Sly
8b5521ec0a Replace unparsable apostrophe character with ASCII "'". (#46069) 2024-08-28 18:34:06 +02:00
Mikael Simberg
b9e4e98f15 boost: install BoostConfig.cmake even when header-only (#46062)
* Install BoostConfig.cmake even when header-only

* boost: Only use --without-libraries when --with-libraries would have an empty list
2024-08-28 17:44:02 +02:00
Harmen Stoppels
85487f23bc buildcache.py: elide string not spec (#46074) 2024-08-28 15:27:44 +02:00
Harmen Stoppels
fb4811ec3f Drop now redundant use of inspect (#46057)
inspect.isclass -> isinstance(..., type)
inspect.getmro -> cls.__mro__
2024-08-28 14:35:08 +02:00
Mark Abraham
202e64872a gromacs: add conflict between NVSHMEM and cuFFTMp (#46065)
* Add conflict between NVSHMEM and cuFFTMp for GROMACS package

These don't work in the same build configuration.

* [@spackbot] updating style on behalf of mabraham

* Update package.py

Also constrain NVSHMEM appropriately

* Update var/spack/repos/builtin/packages/gromacs/package.py

Co-authored-by: Andrey Alekseenko <al42and@gmail.com>

* Update package.py

* [@spackbot] updating style on behalf of mabraham

* Update package.py

* Update package.py

* Update package.py

* [@spackbot] updating style on behalf of mabraham

---------

Co-authored-by: mabraham <mabraham@users.noreply.github.com>
Co-authored-by: Christoph Junghans <christoph.junghans@gmail.com>
Co-authored-by: Andrey Alekseenko <al42and@gmail.com>
2024-08-28 04:34:37 -06:00
Massimiliano Culpo
25ba3124bd Spec.from_detection now accounts for external prefix (#46063)
Change the signature of Spec.from_detection to set the
external prefix, and the external modules, if they are
present.

Delete "spack.package_prefs.spec_externals" since it
is unused.
2024-08-28 10:51:36 +02:00
Massimiliano Culpo
df57e1ceb3 Remove llnl.util.lang.has_method, use built-in hasattr instead (#46072) 2024-08-28 10:17:12 +02:00
Adam J. Stewart
59b4b785e0 py-numpy: add v2.0.2 (#46056) 2024-08-27 11:20:42 -07:00
Brian Spilner
b1b21a4d02 cdo-2.4.3 (#46033)
cdo-2.4.3 - fixed hash
2024-08-27 11:17:16 -07:00
Ye Luo
b1af32cb60 Introduce offload variant for llvm >= 19. (#45865) 2024-08-27 13:44:01 -04:00
Harmen Stoppels
9d8f94a7c8 spack_yaml: delete custom deepcopy (#46048) 2024-08-27 18:45:44 +02:00
Massimiliano Culpo
1297673a70 Remove "prevent_cray_detection" context manager (#46060)
This context manager was used to prevent detecting a platform
as "cray". Since now Cray machines are detected as linux, we can
remove the context manager.
2024-08-27 18:43:07 +02:00
Harmen Stoppels
0fee2c234e config.py: tell don't ask (#46040) 2024-08-27 15:55:44 +02:00
Alec Scott
229cf49c71 bfs: add v4.0.1 (#46044)
* bfs: add v4.0.1

* fix style
2024-08-27 08:15:38 -05:00
Massimiliano Culpo
394e6159d6 Remove a few unused classes and globals (#46054) 2024-08-27 15:06:39 +02:00
Stephen Nicholas Swatman
cbe18d9cbc detray: add version 0.73.0 (#46053)
This commit adds version 0.73.0 of the detray package. As this version
drops support for pre-C++20 standards, I had to update the `cxxstd`
variant logic.
2024-08-27 08:05:06 -05:00
Stephen Nicholas Swatman
2d83707f84 acts: add version 36.2.0 (#46052)
This commit adds version 36.2.0 of ACTS. As far as I can tell, there are
no dependency changes.
2024-08-27 07:18:06 -05:00
Massimiliano Culpo
9a91f021a7 Move spack.compilers._to_dict to Compiler (#46051) 2024-08-27 14:01:50 +02:00
Harmen Stoppels
297e43b097 abi.py: remove (#46047) 2024-08-27 10:38:54 +02:00
FrederickDeny
900765901d Added e4s-cl@1.0.4 (#46043) 2024-08-26 23:26:12 -06:00
Nick Hagerty
680d1f2e58 lammps: improve FFT selection and add fft_kokkos variant (#45969) 2024-08-27 07:15:12 +02:00
Richard Berger
76957f19f9 nvpl-fft: new package (#45985) 2024-08-26 21:42:30 -06:00
AcriusWinter
c7001efeb8 sundials: new test API (#45373)
* sundials: new test API

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-26 15:34:06 -07:00
Harmen Stoppels
a60d1084b1 jsonschema: remove optional deps (#46041) 2024-08-26 22:15:52 +02:00
Harmen Stoppels
497e19f0e3 distro.py: avoid excessive stat calls (#46030) 2024-08-26 18:55:55 +02:00
Pranav Sivaraman
cd6ee96398 parallel-hashmap: add v1.3.12 (#46017)
Co-authored-by: pranav-sivaraman <pranav-sivaraman@users.noreply.github.com>
2024-08-26 10:03:38 -06:00
Wouter Deconinck
904d85b53b fastjson: add v1.2.83, v2.0.52 (#45733)
Co-authored-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-08-26 16:29:02 +02:00
Tamara Dahlgren
199653dd31 Bugfix/hsakmt-roct: use correct version for rocm-core and llvm-amdgpu (#45817) 2024-08-26 16:14:20 +02:00
Matthieu Dorier
fdfb4e9893 mruby: add v3.3.0 (#45964) 2024-08-26 15:33:00 +02:00
Adam J. Stewart
afa76ebbdc py-smp: add v0.3.4 (#45967) 2024-08-26 15:31:53 +02:00
Patrick Diehl
e5c045cc1c kokkos: add v4.4.00 (#45758)
Co-authored-by: Patrick Diehl <diehlpk@lanl.gov>
2024-08-26 15:28:35 +02:00
pauleonix
8c92836c39 cuda: add v12.6 (#45558) 2024-08-26 15:27:07 +02:00
Wouter Deconinck
a782e6bc33 r-googlesheets4: fix r-gargle dependency (#45980) 2024-08-26 14:37:00 +02:00
mvlopri
4ede0ae5e3 seacas: add parallel as a dependency (#45981) 2024-08-26 14:31:17 +02:00
Wouter Deconinck
986325eb0d r-pbkrtest: fix typo in dependency (#45997) 2024-08-26 14:25:01 +02:00
Wouter Deconinck
8bcd64ce6c r-diagram: fix dependency on non-existent R version (#46003) 2024-08-26 14:11:41 +02:00
Wouter Deconinck
f079ad3690 r-sf: deprecate unconcretizable 0.5-5 (#46016) 2024-08-26 14:10:25 +02:00
Juan Miguel Carceller
8c1d6188e3 gaudi: remove redundant dependency on cppgsl (#46029)
Co-authored-by: jmcarcell <jmcarcell@users.noreply.github.com>
2024-08-26 14:09:48 +02:00
Todd Gamblin
1d70ab934c ci generate: don't warn when no cdash config (#46004)
Right now if you run `spack ci generate` you get a warning about CDash credentials even
if there's no CDash configuration specified. We should only warn if there was actually a
CDash config.

Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2024-08-26 13:58:41 +02:00
Kacper Kornet
fa704e867c polyml: add new package (#46020) 2024-08-26 13:42:30 +02:00
Kacper Kornet
85939b26ae mrbayes: readline and mpi variants are mutually exclusive (#46021) 2024-08-26 13:38:57 +02:00
Wouter Deconinck
a5436b3962 R: external detection (#46023)
Co-authored-by: wdconinc <wdconinc@users.noreply.github.com>
2024-08-26 13:37:35 +02:00
James Shen
a8e25193e0 root: patch v6.22.08 (#46019) 2024-08-26 13:16:25 +02:00
Wouter Deconinck
480d6f9911 cppunit: add v1.15.1; deprecate custom commit version (#46026) 2024-08-26 13:15:29 +02:00
Harmen Stoppels
02f329a8af compilers: avoid redundant fs operations and cache (#46031) 2024-08-26 12:49:58 +02:00
Wouter Deconinck
2de712b35f netfilter pkgs: avoid 3rd party urls, add latest official version (#46027)
Co-authored-by: wdconinc <wdconinc@users.noreply.github.com>
2024-08-26 12:41:11 +02:00
Wouter Deconinck
aa49b3d8ce lshw: add v02.20 (#46028) 2024-08-26 09:46:41 +02:00
Adam J. Stewart
eccecba39a Python: add v3.12.5, default to latest version (#45712)
Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2024-08-25 18:49:01 -07:00
Harmen Stoppels
94c99fc5d4 variant.py: extract spec bits into spec.py (#45941) 2024-08-24 09:45:23 +02:00
John W. Parent
1f1021a47f PythonExtension: use different bin dir on Windows (#45427) 2024-08-24 09:34:24 +02:00
Harmen Stoppels
296e5308a7 mirror: fetch by digest (#45809)
Source mirrors store entries by digest and add a human readable alias of the
form 'name-version'. If no digest is available, the alias is used as the primary
storage location.

Spack erroneously fetches by alias when the digest path does not exist. This is
problematic if `version(..., sha256=...)` changes in package.py, and the mirror
is populated with the old shasum. That would result in an error when a digest
is available, but in case of git versions with a modified commit sha, the wrong
sources would be fetched without error. With this PR, only the digest path is
used, not the alias, in case a digest is available. This is also a small performance
optimization, as the number of request is halved for mirrors that don't contain
the sources.

Further, for git sources the tag was used as a digest, but this is a moving
target. Only commit sha is used now.

Also whenever the alias already existed, Spack used to keep it in place when
updating the mirror cache, which means that aliases would always point to
outdated mirror entries whenever digests are modified. With this PR the alias
is moved in place.

Lastly, fix a recent regression where `Stage.disable_mirrors` disabled mirrors
but not the local download cache, which was the intention.
2024-08-24 09:09:25 +02:00
Massimiliano Culpo
47e79c32fd Substitute __import__ with importlib.import_module (#45965) 2024-08-23 21:41:26 +02:00
David Gardner
906799eec5 add SuperLU_MT v4.0.1 (#45924) 2024-08-23 09:53:21 -07:00
kwryankrattiger
dcdcab7b2c VTK-m: Point to github mirror for source tarball (#45893)
* VTK-m: Point to github mirror for source tarball
  The gitlab.kitware.com source location seems to have intermittent
  network issues. Switching the to mirror hosted at Github may alleviate
  some of the timeouts.
* Update sha256 for GitHub tarballs

---------

Co-authored-by: Zack Galbreath <zack.galbreath@kitware.com>
2024-08-23 09:39:59 -07:00
snehring
86050decb9 gromacs: add env mods for cufftmp w/ gcc (#45887) 2024-08-23 09:37:38 -07:00
Stephen Nicholas Swatman
fff8165f2f davix: add versions 0.8.2-0.8.7 and dependencies (#45853)
* davix: add versions 0.8.2-0.8.7 and dependencies
  This commit adds new versions 0.8.2-0.8.7 of the davix package, and it
  also improves the handling of embedded packages. Davix will try to build
  libcurl from its own embedded version of that code, which doesn't mesh
  well with Spack's design philosophy, so I've changed the CMake
  configuration to disallow the builtin libcurl and use a Spack dependency
  instead. Up to version 0.8.7, RapidJSON was also builtin, but version
  0.8.7 allows users to specify that they want to use a pre-installed
  version of RapidJSON, so this commit also adds that as a dependency for
  versions 0.8.7:.
* Fix old versions
2024-08-23 09:35:48 -07:00
Wouter Deconinck
5a9dbcc0c4 ddt: add v23.0.4 -> v24.0.3 (#45861)
* ddt: add v23.0.4 -> v24.0.3
* ddt: fix url_for_version for 22.1.3

---------

Co-authored-by: Lydéric Debusschère <lyderic.de@gmail.com>
2024-08-23 09:12:42 -07:00
Adam J. Stewart
bd627465f3 py-autograd: mark numpy 2 compatibility (#45942)
* py-autograd: mark numpy 2 compatibility

* Fix syntax error
2024-08-23 09:08:15 -07:00
Stephen Nicholas Swatman
f96e8757b8 acts: add v36.0.0, v36.1.0 and fixes (#45874)
* acts: add v36.0.0, v36.1.0 and fixes

This commit makes several changes to the Acts repository, namely:

1. It adds versions 36.0.0 and 36.1.0.
2. It adds the traccc plugin and related dependencies.
3. It updates the version requirements of some dependencies.
4. It adds the Geant4 module of GeoModel.
5. It updates the C++ standard requirement.
6. It adds a new variant determining the scalar type to use.

This commit supercedes #45851. Thanks @jmcarcell for the version
updates; I have added you as co-author.

Co-authored-by: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com>

* Updates

* alphabetic cmake args

---------

Co-authored-by: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com>
2024-08-23 07:09:29 -06:00
Harmen Stoppels
b8cbbb8e2e spack create: add depends_on(<lang>) statements (#45296) 2024-08-23 10:33:05 +02:00
Harmen Stoppels
d40f847497 Add missing MultiMethodMeta metaclass in builders (#45879)
* Add missing MultiMethodMeta metaclass in builders

and remove the Python 2 fallback option in favor of hard errors to catch
similar issues going forward.

The fallback option can cause about 10K stat calls due to use of
`realpath` in the inspect module, depending on how deep Spack itself is
nested in the file system, which is ... undesirable.

* code shuffling to avoid circular import

* more reshuffling

* move reserved variant names into variants module
2024-08-23 09:23:25 +02:00
Wouter Deconinck
ed34dfca96 xrootd: change urls to xrootd.web.cern.ch (#45895)
* xrootd: change urls to xrootd.web.cern.ch
* xrootd: change homepage
2024-08-22 20:02:14 -06:00
Christopher Christofi
3ee6a5b96f py-ipykernel: add version 6.29.5 (#45876)
* py-ipykernel: add version 6.29.5
* add maintainers for spack package
2024-08-22 18:24:38 -07:00
Kyoko Nagahashi
88bcfddbbb New package: linux-external-modules (#45797) 2024-08-22 19:20:01 -06:00
Juan Miguel Carceller
c49269f9dd poppler: change the URL of the test repository (#45857)
* poppler: change the URL of the test repository
2024-08-22 18:03:28 -07:00
Adam J. Stewart
ef45c392e0 py-scipy: add v1.14.1 (#45847) 2024-08-22 18:00:57 -07:00
AMD Toolchain Support
8b811171c7 removing -Ofast with aocc (#45880)
Co-authored-by: shbhaska <shbhaska@amd.com>
2024-08-22 17:54:58 -07:00
Richard Berger
823a2c1e4b kokkos-tools: add new package (#45382) 2024-08-22 17:09:19 -07:00
Massimiliano Culpo
ead25b1e9e Add a new audit to find missing package.py files (#45868)
* Add a new audit to find missing package.py files

* Remove directory without package.py
2024-08-22 14:22:54 -07:00
Stephen Nicholas Swatman
d5eefcba87 llvm-amdgpu: Conflict with MacOS (#45633)
Currently, the llvm-amdgpu package doesn't compile on MacOS, but it is
also not marked as a conflict. This causes problems because it seems
that Spack is very happy to pull in llvm-amdgpu as the default package
to satisfy any virtual libllvm dependency, which can cause dependent
specs to fail to install on MacOS. This commit marks a conflict between
this llvm package and the Darwin platform.
2024-08-22 11:14:20 -06:00
Nicole C.
1bcb1fcebc Windows: port tests for mirror/blame (#45259) 2024-08-22 09:49:32 -07:00
John W. Parent
f19b657235 VTK package: patch to fix NetCDFC - HDF5 interface (#43087)
Patch from Windows is also needed on Linux
2024-08-22 09:48:13 -07:00
Stephen Nicholas Swatman
8e1bd9a403 root: remove +webgui~http conflict version clause (#45856)
* root: set +webgui~http conflict from @6.28.12:

Currently, the ROOT spec correctly identifies a conflict between +webgui
and ~http, but this conflict is marked as affecting @6.29.00: only. As a
matter of fact, ROOT 6.28.12 is also affected. This commit, therefore,
updates the when clause on the conflict to @6.28.12:.

* Remove when clause entirely

* oops
2024-08-22 08:14:09 -05:00
Stephen Nicholas Swatman
ba56622574 geomodel: fix bug in cmake_args (#45869) 2024-08-22 05:13:21 -06:00
Massimiliano Culpo
836be2364c Make spack compiler find use external find (#45784)
so that there is no duplicate detection logic for compilers
2024-08-22 12:13:08 +02:00
Juan Miguel Carceller
b623f58782 root: add version 6.32.04 (#45850)
Co-authored-by: jmcarcell <jmcarcell@users.noreply.github.com>
2024-08-21 16:43:48 -07:00
John W. Parent
182bc87fe1 Windows: Port icu4c; define cxx std flags for MSVC (#45547)
* Adds an MSBuild system + Builder to the icu4c package
* Adds custom install method as MSBuild system does not vendor an
  install target
* The cxxstd variant is not supported on Windows (there are no config
  options you use to tell the build system what cxx standard to
  build against), so the variant definition was updated to occur
  everywhere except Windows

Also, this commit defines the c/cxx..._flag properties of the MSVC
compiler (although they are not used by `icu4c` and not strictly
necessary to bundle with this PR).
2024-08-21 16:08:57 -06:00
Stephen Nicholas Swatman
f93595ba2f acts: add GeoModel dependency (#45859)
This commit adds a dependency on GeoModel 4.6.0 when the GeoModel plugin
is enabled. Note that the dependency is upgraded to 6.3.0 in Acts
36.1.0, but that will need to be covered in #45851.
2024-08-21 11:27:54 -06:00
Adam J. Stewart
aa5b17ceb5 py-shapely: add v2.0.6 (#45833) 2024-08-21 09:47:24 -07:00
Dom Heinzeller
eb5a1d3b4c Add fms@2024.02 (#45838) 2024-08-21 09:44:40 -07:00
Wouter Deconinck
84f680239e geoip: deprecate due to duplication (#45840)
* geoip: deprecate due to duplication
* geoip-api-c: fixed hashes; checked license; verified c code

---------

Co-authored-by: wdconinc <wdconinc@users.noreply.github.com>
2024-08-21 09:42:54 -07:00
Stephen Nicholas Swatman
c7b693a0df geomodel: add versions 5.1.0-6.4.0 (#45858)
* geomodel: add versions 5.1.0-6.4.0

This PR adds new versions 5.1.0 through 6.4.0 of the geomodel package.
It also updates the CMake configuration to use the `define_from_variant`
mechanism and it adds me as a maintainer.

* Undo cmake change
2024-08-21 10:41:36 -06:00
Alex Seaton
7d5ad18573 heyoka: add v5.1.0 (#45841) 2024-08-21 09:30:01 -07:00
Stephen Herbener
2921e04353 Added latest version of eckit (#45834) 2024-08-21 09:01:54 -07:00
Massimiliano Culpo
33464a7038 gcc: simplify version_regex, change string to filter out Apple clang (#45852) 2024-08-21 16:36:07 +02:00
Juan Miguel Carceller
d3cdb2a344 sherpa: add v3.0.0, remove deprecated @:2.2.10 (#45101)
* Remove deprecated versions

* Add sherpa 3.0.0 and CMake builds

* Address comments in #45101

* Add builder classes for cmake and autotools

---------

Co-authored-by: jmcarcell <jmcarcell@users.noreply.github.com>
2024-08-21 08:41:18 -05:00
Massimiliano Culpo
34df21b62c gcc: restore old detection (#45810) 2024-08-21 11:31:52 +02:00
Wouter Deconinck
e8a13642a0 packages/*: http -> https where permanent redirect (#45835)
* *: http -> https where permanent redirect

* fix: style
2024-08-21 09:18:24 +02:00
Lin Guo
dc3c96dd2f orca: add 6.0.0 avx2 version (#45824)
The avx2 version can be downloaded from the ORCA [forum](https://orcaforum.kofo.mpg.de/app.php/dlext/?view=detail&df_id=214#).

The version is named `avx2-6.0.0` (as opposed to the more
natural-looking `6.0.0-avx2`) to avoid the avx2 version shadowing the
non-avx2 one. Definitely open for better suggestion.
2024-08-20 18:41:30 -07:00
Victor Brunini
c29652580a arborx: Enable use of Kokkos from Trilinos for cuda/rocm. (#45220) 2024-08-20 15:48:42 -07:00
Matt Thompson
d714a9b223 mapl: add 2.47.2, 2.46.3 (#45795) 2024-08-20 13:10:09 -07:00
Marcel Koch
f596a8cdad ginkgo: add v1.8.0 (#45791)
* bump[Ginkgo]: add version 1.8.0
* [Ginkgo] add patch for rocthrust
* [Ginkgo] change maintainer
* [Ginkgo] use patch from PR
* [Ginkgo] fix style issues

---------

Co-authored-by: Terry Cojean <terry.cojean@kit.edu>
2024-08-20 09:36:51 -07:00
Vicente Bolea
3699a0ec9b paraview: add new v5.13.0-RC2 release (#45754) 2024-08-20 10:52:35 -05:00
Vicente Bolea
6c268234ba paraview: add smoke tests (#45759) 2024-08-20 10:46:45 -05:00
Greg Becker
c1736077bb spack bootstrap status --dev: function call for new interface (#45822) 2024-08-20 13:04:39 +00:00
psakievich
85905959dc Increase min version for sparse_checkout (#45818)
* Increase min version for sparse_checkout

* Update git_fetch.py

* style
2024-08-20 13:04:23 +00:00
Harmen Stoppels
2ae5596e92 Unify url and oci buildcache push code paths (#45776) 2024-08-20 13:17:49 +02:00
Sajid Ali
9d0b9f086f Fix linking for python with external ncurses (#45803)
Co-authored-by: Adam J. Stewart <ajstewart426@gmail.com>
2024-08-20 06:09:55 +02:00
Mikael Simberg
da079ed06f ninja: add 1.12.1 (#45789) 2024-08-19 17:14:13 -06:00
Fernando Ayats
a69c5b3e32 freefem: add v4.13, v4.12 and v4.11 (#45808) 2024-08-19 13:21:10 -07:00
Massimiliano Culpo
e3cce2bd96 binutils: add v2.43.1 (#45806) 2024-08-19 13:40:51 -06:00
AcriusWinter
0d668e4e92 hsakmt-roct: remove use of deprecated run_test method (#45763)
* hsakmt-roct: new test API
* hsakmt-roct: minor change to check_install script variable name

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-19 12:20:54 -07:00
David Gardner
ad6c7380c5 silo: add python variant (#45757)
* add python variant

* use enable_or_disable

* use extend
2024-08-19 10:56:44 -07:00
Adam J. Stewart
c064a30765 py-numpy: add v2.1.0 (#45807) 2024-08-19 10:25:37 -07:00
AcriusWinter
4a4f156d99 parallel-netcdf: new test API (#45170)
* parallel-netcdf: new test API
* parallel-netcdf: fix test args and tweak docstring and variables

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-19 09:39:42 -07:00
AcriusWinter
cb8878aaf4 hipsolver: remove use of deprecated run_test method (#45761)
* hipsolver: new test API
2024-08-19 09:38:19 -07:00
Vicente Bolea
d49f3a0960 diy: add smoke test (#45749)
Installing examples for running smoke tests for the DIY project.
2024-08-19 11:04:09 -05:00
Massimiliano Culpo
15413c7258 llvm based compilers: filter out non-compilers (#45805) 2024-08-19 09:28:37 -06:00
Teague Sterling
de754c7a47 perl-bio-bigfile: new package (#44505)
* Adding the perl-bio-db-bigfile package

* Update package.py

* Update package.py

* Update package.py

* Updating dependent package handling

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Updating dependent package handling

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Reverting variants

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Rename package.py to package.py

* Update package.py

* Removing unneeded dependencies

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

---------

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>
2024-08-19 16:14:49 +01:00
Harmen Stoppels
ac9398ed21 build_environment: explicitly disable ccache if disabled (#45275) 2024-08-19 13:49:38 +02:00
Harmen Stoppels
57769fac7d Simplify URLFetchStrategy (#45741) 2024-08-19 11:34:13 +02:00
Wouter Deconinck
c65fd7e12d apfel: add v3.1.1 (now CMakePackage) (#45661)
Co-authored-by: wdconinc <wdconinc@users.noreply.github.com>
Co-authored-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-08-19 10:45:49 +02:00
snehring
c71d778875 salmon: add v1.10.3 (#45695)
Signed-off-by: Shane Nehring <snehring@iastate.edu>
2024-08-19 10:38:56 +02:00
Luke Robison
a7313dc407 WRF: add -fpermissive when using gcc@14: (#45438) 2024-08-19 10:30:18 +02:00
Wouter Deconinck
31477d5dc7 activeharmony: replace dead links (#45704) 2024-08-19 10:28:57 +02:00
Wouter Deconinck
382ba0d041 mlpack: add through v4.4.0 (#45707) 2024-08-19 10:26:10 +02:00
Adam J. Stewart
886c950423 py-keras: add v3.5 (#45711) 2024-08-19 10:11:08 +02:00
Matthias Wolf
3798b16a29 py-brain-indexer: new package (#44718) 2024-08-19 10:09:43 +02:00
Matt Thompson
796617054d py-pyyaml: add v6.0.2 (#45716) 2024-08-19 10:05:39 +02:00
Pranav Sivaraman
78fc25ec12 magic-enum: fix minimum compiler versions conflicts (#45705) 2024-08-19 10:04:09 +02:00
Wouter Deconinck
6de51fdc58 librsvg: depends_on cairo +png (#45729) 2024-08-19 09:55:28 +02:00
snehring
430ba496d1 liftoff: add new package (#45726)
Signed-off-by: Shane Nehring <snehring@iastate.edu>
2024-08-19 09:48:47 +02:00
Wouter Deconinck
e1ede9c04b bind9: add v9.18.28, v9.20.0 (#45728) 2024-08-19 09:47:16 +02:00
Wouter Deconinck
856dd3417b gradle: add through v8.9 (#45731) 2024-08-19 09:44:45 +02:00
Wouter Deconinck
e49c6f68bc maven: add v3.8.8, v3.9.8 (#45732) 2024-08-19 09:44:00 +02:00
Alex Leute
eed7a1af24 mlc-llm: new package and dependency (#44726) 2024-08-19 09:33:00 +02:00
Rocco Meli
22e40541c7 CP2K: add 2024.2, fix dbcsr+g2g+plumed (#45614) 2024-08-19 09:19:17 +02:00
Wouter Deconinck
8561c89c25 hadoop: add v3.3.3 -> v3.4.0 (#45735) 2024-08-19 09:05:26 +02:00
dslarm
6501705de2 armpl-gcc - finish enabling debian12 (#45744) 2024-08-19 09:01:09 +02:00
Wouter Deconinck
0b3e1fd412 openssh: add v9.8p1 (#45736)
Co-authored-by: wdconinc <wdconinc@users.noreply.github.com>
2024-08-19 08:52:09 +02:00
Wouter Deconinck
c260da5127 shared-mime-info: fix url for certificate name mismatch (#45779) 2024-08-19 08:42:03 +02:00
Wouter Deconinck
f63261dc65 ghostscript: add v10.01.2, v10.02.1, v10.03.1 (#45780) 2024-08-19 08:40:56 +02:00
Wouter Deconinck
1c081611ea graphviz: add v8.1.0 -> v12.1.0 (#45675) 2024-08-19 08:33:02 +02:00
Alec Scott
428b4e340a Remove deprecated --safe-only in spack version cmd (#45765) 2024-08-19 08:28:19 +02:00
Wouter Deconinck
20bf239a6a xorg-server: add variants dri and glx (#45787)
Co-authored-by: wdconinc <wdconinc@users.noreply.github.com>
2024-08-19 08:07:56 +02:00
Massimiliano Culpo
cd682613cf dbcsr: avoid using a set in a message (#45804) 2024-08-19 07:35:33 +02:00
Axel Huebl
c1852e3706 WarpX: Python on pyAMReX (#45251)
* WarpX: Python on pyAMReX

Long overdue update for WarpX: in 2024, we updated our Python
bindings to rely on the new pyAMReX package. This deprecates the old
`py-warpx` package and adds a new dependency and variant to WarpX.

Also deprecates old versions that we will not continue to support.

* Update Cloud/E4S Pipelines for WarpX

`py-warpx` is replaced by `warpx +python`
oneAPI does not support IPO/LTO: diable for `py-amrex` even though
pybind11 strongly encourages it.
2024-08-18 21:14:04 -07:00
Rémi Lacroix
855a8476e4 Scotch: Fix sha256 for some older versions. (#44494)
Most likely caused by a change in Inria's Gitlab.
2024-08-18 21:18:00 +02:00
Auriane R.
d4a892f200 py-torch-nvidia-apex: Add 24.04.01 and variants from the readme (#45019)
Co-authored-by: Bernhard Kaindl <bernhardkaindl7@gmail.com>
2024-08-18 21:16:58 +02:00
Harmen Stoppels
66e2836ba1 py-torchaudio: upperbound on cuda (#45773)
* py-torchaudio: upperbound on cuda

* actually get bound right

* make adam happy
2024-08-17 11:18:32 -06:00
Teague Sterling
52ab0c66fe xorgproto: new package (#45569)
* xorgproto: new package

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* adding providers for xorgprotos

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>

* Update var/spack/repos/builtin/packages/xorgproto/package.py

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* Update etc/spack/defaults/packages.yaml

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* Update etc/spack/defaults/packages.yaml

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* Update var/spack/repos/builtin/packages/xorgproto/package.py

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* Update var/spack/repos/builtin/packages/xorgproto/package.py

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* Update var/spack/repos/builtin/packages/xorgproto/package.py

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* Update var/spack/repos/builtin/packages/xorgproto/package.py

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

* [@spackbot] updating style on behalf of teaguesterling

* xorgproto: depends_on meson type build

---------

Signed-off-by: Teague Sterling <teaguesterling@gmail.com>
Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-08-17 10:24:28 -06:00
Stephen Hudson
f316068b27 libEnsemble: add v1.4.2 (#45760) 2024-08-17 08:25:00 -05:00
Wouter Deconinck
553cc3b70a util/web.py: parse new GitLab JS dropdown links (#45764)
Co-authored-by: Harmen Stoppels <harmenstoppels@gmail.com>
2024-08-17 09:02:03 +02:00
Chris Marsh
f0f9a16e4f esmf package: add (optional) Python bindings (#45504)
* Add `+python` variant
* `esmf` package installs Python bindings when `+python` is set

Note: this does not inherit `PythonPackage`, which force an either/or
choice between the Makefile and Pip builder: it instantiates a
`PythonPipBuilder` as needed (when `+python` is set).
2024-08-16 15:02:50 -07:00
Greg Becker
9ec8eaa0d3 include_concrete: read from older env formats properly (#45766)
* include_concrete: read from older env formats properly
* spack env rm: fix logic for checking env includes
* regression test

Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2024-08-16 20:40:41 +00:00
Adam J. Stewart
00182b19dc GDAL: add v3.9.2 (#45794) 2024-08-16 13:26:35 -07:00
James Smillie
cc7a29c55a Windows: fix stage cleaning for long paths (#45786)
Paths over 260 characters in length are not handled by `shutil.rmtree`
unless they use the extended-length path syntax (using a prefix of
"\\?\").

This fixes an issue where stage cleaning fails when paths in a stage
exceed the normal 260-character limit.

This indicates that other parts of the codebase should be examined/
refactored to handle long paths.
2024-08-16 11:16:13 -07:00
eugeneswalker
61b0f4f84d e4s ci: add wrf (#45719)
* e4s ci: add wrf

* e4s ci: also add wrf companion/adjacent package wps

* e4s oneapi: comment out wps: %oneapi not supported?
2024-08-16 09:57:46 -07:00
Massimiliano Culpo
fe3bfa482e Run unit test in parallel again in CI (#45793)
The --trace-config option was failing for linux unit-tests,
so we were running serial.
2024-08-16 16:11:08 +00:00
Paul R. C. Kent
e5f53a6250 py-lxml: add v5.2.2 (#45785)
* add v5.2.2

* py-lxml dependency improvements

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>

---------

Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-08-16 10:04:29 -06:00
bk
a7e8080784 harfbuzz: enable freetype in MesonBuilder (#45654)
* harfbuzz: enable freetype in MesonBuilder to facilitate depends_on("freetype")

* spack style fix

* freetype is defined as a depends_on(), so set as enabled in MesonBuilder rather than an option/flag/variant

* add back depends_on($lang) lines for new test api

* keep original order
2024-08-16 10:59:27 -05:00
Wouter Deconinck
f5e934f2dc *: avoid js redirect for homepages on sourceforge (#45783) 2024-08-16 17:35:04 +02:00
Harmen Stoppels
54b57c5d1e Revert "Change environment modifications to escape with double quotes (#36789)" (#42780)
This reverts commit 690394fabc, as it causes arbitrary code execution.
2024-08-16 17:32:48 +02:00
Harmen Stoppels
725ef8f5c8 oci: support --only=package (#45775)
Previously `spack buildcache push --only=package` errored in the OCI
case, but it's been requested that OCI can be used as pure storage w/o
the need for runnable container images.

This commit makes it so that

1. manifests refer only to runtime dependencies that were selected to be
   pushed
2. failure to upload a blob among the selected specs does not prevent a
   manifest/tag to be created for dependents: they just don't refer to
   the missing blob as a layer/dependency

This fixes the following issues:

1. dependents of non-redistributable specs can now be pushed to oci
   build caches without error
2. failure to upload one tarball does not cause cascading failures for
   dependents whose tarballs do upload succesfully -- so it's better
   best-effort behavior
3. for some people uploading with deps caused a massive amount of
   fetches of their manifests (which certain registries count as a
   download of an image, even though their layers are not fetched) --
   being able to specify --only=package reduces the number of fetches.
2024-08-16 15:24:04 +02:00
Harmen Stoppels
f51a9a9107 stage: provide mirrors in constructor (#45792)
Stage objects create mirrors ad-hoc from current config.

- There is no way to prevent mirrors from being used
- There is no way to restrict mirrors to source/binary, which is of
  course context dependent.
- Stage is also used in build caches, where iterating over mirrors is
  already implemented differently, and wouldn't work anyways cause it's
  source only, and in particular it makes no sense for OCI build caches.

This commit:

1. Injects the sensible mirrors into the stage object from contexts
   where it is relevant
2. Separates mirrors from cache, so that w/o mirrors download cache can
   still be used
2024-08-16 15:21:47 +02:00
Massimiliano Culpo
4f0e336ed0 Remove "test_foreground_background" 2024-08-16 14:22:59 +02:00
Massimiliano Culpo
64774f3015 Skip test_foreground_background + other minor cleanups
The test_foreground_background unit test has been marked
xfail for a while, meaning:
- Nobody looks at the results of the test
- It still runs every time

That test happens to hang frequently on some Apple M1 I have access to,
so here I mark it as skip.

Also went through other xfailing and skipped tests, and applied minor changes.
2024-08-16 14:22:59 +02:00
Massimiliano Culpo
4e9fbca033 Clean up test/cmd/ci.py (#45774)
* Use absolute paths instead of https:// fake mirrors (this speed-up tests by avoiding requests)
* Add a fixture to gather in a single place code that is copy/pasted in a lot of tests
* General clean-up of tests and repeated code

Co-authored-by: Harmen Stoppels <me@harmenstoppels.nl>
2024-08-16 13:42:01 +02:00
AcriusWinter
a2fd26bbcc rccl: new test API (#45241)
* rccl: new test API
* rccl: stand-alone test docstring tweak

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-15 22:03:05 -06:00
AcriusWinter
067da09b46 hypre: get rid of use of deprecated run_test method (#45762)
* hypre: get rid of depreciated run_test method
* hypre: use mkdirp directly
* hypre: use install() for ij for addition of permissions fix

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-15 20:37:39 -06:00
AcriusWinter
b1b0c108bb parsec: old to new test API (#45122)
* parsec: old to new test API
* parsec: restore stand-alone test subparts; preliminary test build fixes

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-15 18:22:55 -06:00
AcriusWinter
c624088a7b n2p2: convert from old to new test API (#45141)
* n2p2: convert from old to new test API
* n2p2: Enhance stand-alone testing checks to reduce unnecessary processing

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-15 15:47:55 -06:00
AcriusWinter
a965c7c5c8 Open3d: Reinstate re-use of stand-alone test method (#45755)
* open3d: Reinstate re-use of stand-alone test method
* open3d: ignore test_open3d_import when ~python

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-15 13:52:31 -07:00
Greg Sjaardema
904d43f0e6 seacas: new version (#45777)
Update fmt dependency to support fmt@11:
Use an adios2 release instead of master
New release of seacas
2024-08-15 15:21:26 -05:00
AcriusWinter
10b6d7282a Cache extra test sources update (#45493)
* stand-alone test API update: self.cache_extra_test_sources(...) -> cache_extra_test_sources(self, ...)
* superlu: switch to new cache_extra_test_sources API

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-15 12:55:16 -06:00
Richard Berger
7112a49d1e libmesh: explicitly disable metis in ~metis case (#45781) 2024-08-15 11:28:21 -07:00
Mikael Simberg
b11bd6b745 pika: add 0.27.0 (#45772) 2024-08-15 11:11:19 -07:00
Derek Ryan Strong
4d0b04cf34 hwloc: add v2.11.1 (#45767)
* Add hwloc v2.11.1
2024-08-15 11:10:10 -07:00
Massimiliano Culpo
165c171659 Update archspec to v0.2.5-dev (7e6740012b897ae4a950f0bba7e9726b767e921f) (#45721) 2024-08-15 19:49:07 +02:00
David Gardner
aa3c62d936 limit patch range (#45756) 2024-08-15 15:08:24 +02:00
Alex Richert
cba2fe914c g2: add 3.5.0 and 3.5.1 (#45750) 2024-08-14 23:43:40 -06:00
psakievich
1b82779087 Add options for sparse checkout in GitFetcher (#45473)
* Add options for sparse checkout in GitFetcher

Newer versions of git have a beta feature called sparse checkout
that allow users to check out a portion of a large repo.

This feature will be ideal for monolithic repo projects that want to
model their infrastructure via spack.  This PR implements an addition
to the GitFetcher that allows users to add a `git_sparse_paths`
attribute to package classes or versions which will then use sparse
checkout on those directories/files for the package.

* Style

* Split git clone into multiple functions

* Add sparse-checkout impl

* Internalize src clone functions

* Docs

* Adding sparse clone test

* Add test for partial clone

* [@spackbot] updating style on behalf of psakievich

* Small fixes

* Restore default branch status

* Fix attributes for package

* Update lib/spack/docs/packaging_guide.rst

Co-authored-by: Matthew Mosby <44072882+mdmosby@users.noreply.github.com>

* Extend unit test to multiple git versions

* style

---------

Co-authored-by: psakievich <psakievich@users.noreply.github.com>
Co-authored-by: Matthew Mosby <44072882+mdmosby@users.noreply.github.com>
2024-08-15 05:28:34 +00:00
Evan Parker
55b1b0f3f0 py-fortranformat: update to version 2.0.0 (#45748)
* Feature update py-fortranformat
  Add more recent versions of py-fortranformat. The currently included release (0.2.5) is from 2014. I've added the latest point release of each of the major versions from the last 4 years.
* update homepage
2024-08-14 23:14:02 -06:00
AcriusWinter
4606c8ed68 magma: old to new test API (#45140)
* magma: old to new test API
* magma: simplify stand-alone test method/part docstrings/purposes 

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-14 19:37:12 -06:00
AcriusWinter
dd53eeb322 libpressio: old to new test API (#45151)
* libpressio: old to new test API
* libpressio: minor stand-alone test simplifications

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-14 15:59:08 -07:00
Greg Becker
f42486b684 spack info: use spec fullname (#45753) 2024-08-14 22:00:00 +00:00
Alex Richert
44ecea3813 g2tmpl: add 1.13.0 (#45751) 2024-08-14 15:25:57 -06:00
eugeneswalker
f1114858f5 e4s ci: add chapel (#45659)
* e4s ci: add chapel
* e4s ci: fix gpu target typo

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-14 09:54:07 -07:00
Greg Becker
2b6bdc7013 OneapiPackage: do not use getpass.getuser (#45727)
* OneapiPackage: do not use getpass.getuser
2024-08-14 09:48:44 -07:00
Massimiliano Culpo
586a35be43 SpecHashDescriptor: better repr in debugger (#45739) 2024-08-14 18:02:09 +02:00
Fernando Ayats
7a8dc36760 freefem: add v4.14 (#45687) 2024-08-14 08:49:04 -07:00
afzpatel
e01151a200 enable asan in remaining rocm packages (#45192)
* initial commit to enable asan in remaining rocm packages
* remove os import
* add rocm-opencl rocm-dbgapi rocm-debug-agent
* add libclang path to LD_LIBRARY_PATH
* enable asan for rocfft
* add f-string and add +asan dependencies for hip and rocm-opencl
* add conflicts for centos7/8 and rhel 9
2024-08-14 08:46:04 -07:00
Harmen Stoppels
29b50527a6 spack buildcache push: parallel in general (#45682)
Make spack buildcache push for the non-oci case also parallel, and --update-index more efficieny
2024-08-14 17:19:45 +02:00
Massimiliano Culpo
94961ffe0a Optimize marshaling of Repo and RepoPath (#45742)
When sending Repo and RepoPath over to a child process,
we go through a marshaling procedure with pickle. The
default behavior for these classes is highly inefficient,
as it serializes a lot of specs that can just be
reconstructed on the other end of the pipe.

Here we write optimized procedures to __reduce__ both
classes.
2024-08-14 14:34:35 +02:00
Massimiliano Culpo
03a7da1e44 Micro-optimize finding executables (#45740) 2024-08-14 13:52:28 +02:00
Massimiliano Culpo
97ffe2e575 Add schema for compiler options in packages.yaml (#45738) 2024-08-14 11:47:36 +02:00
Harmen Stoppels
7b10aae356 Show underlying errors on fetch failure (#45714)
- unwrap/flatten nested exceptions
- improve tests
- unify curl lookup
2024-08-14 08:15:15 +00:00
AcriusWinter
b61cd74707 raja: new test API (#45184)
* raja: new test API
* raja: tweak test method names and docstrings
* raja: restore running tests under proper directory
* raja: cleanup skiptest message and example call
* raja: Tweak expected outputs to match current
* raja: test_views -> test_stencil_offset_layout

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-13 21:36:14 -06:00
Adam J. Stewart
374d94edf7 py-matplotlib: add v3.9.2 (#45710) 2024-08-13 22:15:00 -05:00
dependabot[bot]
827522d825 build(deps): bump docker/build-push-action from 6.6.1 to 6.7.0 (#45730)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.6.1 to 6.7.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](16ebe778df...5cd11c3a4c)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-13 18:03:26 -06:00
Tamara Dahlgren
8ba6e7eed2 Bugfix: allow test_* build-time and stand-alone tests (#45699) 2024-08-13 16:58:00 -07:00
AcriusWinter
e40c10509d mptensor: Changed skiptest, test name, and added docstring (#44909)
* mptensor: Changed skiptest, test name, and added docstring
* mptensor: make stand-alone test method name and docstring more specific

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-13 17:48:25 -06:00
Sakib Rahman
21a2c3a591 py-htgettoken: add v2.0-2 (#45688)
* Add version 2.0.-2

* Newer version at the top

* [@spackbot] updating style on behalf of rahmans1

* py-htgettoken: reorder comments

* Use sha256sum instead of commit id for version 2.0-2 and above

---------

Co-authored-by: rahmans1 <rahmans1@users.noreply.github.com>
Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
2024-08-13 14:39:51 -06:00
Chris Marsh
70eb7506df Add py-dask and py-distributed 2024.7.1 (#45546)
* Add dask 2024.3 and distributed 2024.7

* [@spackbot] updating style on behalf of Chrismarsh

---------

Co-authored-by: Chrismarsh <Chrismarsh@users.noreply.github.com>
2024-08-13 14:27:47 -05:00
Massimiliano Culpo
2b95eecb83 Improve external detection tests for compilers (#45709)
Extracted from #44419

This adds / modifies some external detection tests for compilers,
to reproduce cases that are currently tested in unit tests.

The unit tests will later be removed.
2024-08-13 18:37:54 +02:00
Fernando Ayats
df8507f470 bigdft : add v1.9.5 (#45270) 2024-08-13 09:25:34 -07:00
Ganesh Vijayakumar
645c8eeaeb Update OpenFAST package.py (#45706)
Mandating build of C++ driver program whenever "cxx" option is used. Necessitated by recent change to OpenFAST https://github.com/OpenFAST/openfast/blob/dev/glue-codes/openfast-cpp/CMakeLists.txt#L60
2024-08-13 08:04:04 -06:00
Harmen Stoppels
b693987f95 cuda: drop preference (#45130) 2024-08-13 14:17:54 +02:00
BOUDAOUD34
7999686856 siesta: add v4.1.5, v5.0.0 and v5.0.1, add variants and build v5 using cmake (#45518)
Co-authored-by: Bernhard Kaindl <bernhardkaindl7@gmail.com>
2024-08-13 12:10:22 +02:00
Massimiliano Culpo
7001a2a65a Fix a bug with automatic tag detection (#45696)
Extracted from #45638

When adding the "detectable" tag to a package class that has the
"tag" attribute inherited from a base class, we need to copy it to
avoid modifying the base class.
2024-08-13 10:19:26 +02:00
Kaan
7c985d6432 Intel OneAPI Codeplay Plugin for NVIDIA GPU Offload (#45655)
* kickoff attempt

* resource similar to fortran

* delete unused install_component_codeplay

* Adding conflict for versions <= 2022.2.1, moving install to package.py, adding sha256 for version 2024.2.1

* [@spackbot] updating style on behalf of kaanolgu

---------

Co-authored-by: Kaan Olgu <kaan.olgu@bristol.ac.uk>
2024-08-13 08:40:35 +01:00
Harmen Stoppels
a66586d749 spack buildcache push: best effort (#45631)
"spack buildcache push" for partially installed environments pushes all it 
can by default, and only dumps errors towards the end.

If --fail-fast is provided, error out before pushing anything if any
of the packages is uninstalled

oci build caches using parallel push now use futures to ensure pushing
goes in best-effort style.
2024-08-13 08:12:48 +02:00
AcriusWinter
6b73f00310 migraphx: Old to new test API (#44988)
* migraphx: Old to new test API
* migraphx: tweak name and docstring to be more descriptive

---------

Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
2024-08-12 19:29:25 -06:00
Harmen Stoppels
063b987ceb remove config:concretizer:clingo (#45684) 2024-08-12 18:23:14 -07:00
AcriusWinter
fe19394bf9 py-amrex: old to new test API (#45183)
* py-amrex: test name change
2024-08-12 16:56:51 -07:00
Matt Thompson
e09955d83b mepo: Add 2.0.0 (#45691) 2024-08-12 16:54:48 -06:00
Brian Spilner
d367f14d5e add cdo-2.4.1 and cdo-2.4.2 (#45686) 2024-08-12 16:24:06 -06:00
Henri Menke
6f61e382da etsf-io: use pic flag when compiling (#45646) 2024-08-12 16:23:36 -06:00
AcriusWinter
63e680e4f9 c: new test API (#45469)
* c: new test API
* gcc:  provides('c')
* c: bugfix and simplification of the new stand-alone test method

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-12 15:58:09 -06:00
Nicole C.
27557a133b debug: Update cmd and test for Windows (#45309)
* debug: Update cmd and test for Windows

* Add comment tar options not supported by Win tar
2024-08-12 15:50:51 -04:00
dslarm
78810e95ed acfl, armpl-gcc: use ubuntu-22.04 as target for Debian 12 (#45524) 2024-08-12 13:21:40 -06:00
Wouter Deconinck
553b44473f aspell: add v0.60.8.1 (#45685) 2024-08-12 11:38:00 -07:00
Harmen Stoppels
966a775a45 re2: fix cmake cxx std (#45694) 2024-08-12 18:58:02 +02:00
Mikael Simberg
327c75386a mold: add 2.33.0 (#45680) 2024-08-12 07:08:33 -06:00
Cédric Chevalier
a2cb7ee803 Kokkos: only requires a C++ compiler (#45467)
Signed-off-by: Cédric Chevalier <cedric.chevalier@cea.fr>
2024-08-12 06:38:13 -06:00
Wouter Deconinck
2a5d4b2291 cli11: add v2.4.2 (#45673) 2024-08-12 05:07:59 -06:00
Harmen Stoppels
3b59817ea7 deal with TimeoutError from ssl.py (#45683) 2024-08-12 13:06:13 +02:00
Wouter Deconinck
06eacdf9d8 xterm: add v393 (#45678) 2024-08-12 04:27:49 -06:00
Till Ehrengruber
bfdcdb4851 cutensor: add v2.0.1.2 on aarch64 (#45138) 2024-08-12 12:07:53 +02:00
Filippo Spiga
83873d06a1 jube: add v2.6.2, v2.7.0, v2.7.1 (#45599) 2024-08-12 11:59:59 +02:00
G-Ragghianti
91333919c6 SLATE package: make MPI and OpenMP a requirement (#44979)
Co-authored-by: gragghia <gragghia@BlackM3.local>
2024-08-12 11:54:41 +02:00
Melven Roehrig-Zoellner
cd6237cac4 py-pyside2: add version 5.15.14 (#44634)
Co-authored-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
2024-08-12 11:44:28 +02:00
Derek Ryan Strong
91412fb595 openmpi: restrict versions for launcher variants (#45624) 2024-08-12 11:40:02 +02:00
Adam J. Stewart
678c995415 PyTorch: update ecosystem (#45431) 2024-08-12 11:33:27 +02:00
Wouter Deconinck
63af548271 runc: add v1.1.13 (#45679) 2024-08-12 03:33:11 -06:00
Adam J. Stewart
200dfb0346 giflib: fix build on macOS (#45643) 2024-08-12 11:30:45 +02:00
Adam J. Stewart
e2f605f6e9 GDAL: clarify compiler version support (#45651) 2024-08-12 11:27:48 +02:00
Wouter Deconinck
3cf1914b7e py-avro: add v1.11.3, v1.12.0 (#45677) 2024-08-12 03:17:50 -06:00
Wouter Deconinck
cd7a49114c embree: add v4.3.3 (#45674) 2024-08-12 03:13:03 -06:00
Wouter Deconinck
1144487ee7 autodiff: add v1.0.2 -> v1.1.2 (#43527) 2024-08-12 11:03:31 +02:00
Wouter Deconinck
742b78d2b5 wayland-protocols: add v1.35, v1.36; support tests (#45670) 2024-08-12 10:54:10 +02:00
Wouter Deconinck
633d1d2ccb abseil-cpp: add v20240722; support tests (#45671) 2024-08-12 10:50:20 +02:00
Wouter Deconinck
9adefd587e ccache: add v4.10.2 (#45672) 2024-08-12 10:46:55 +02:00
Wouter Deconinck
102a30a5a2 {url,homepage} = http->https://*.sourceforge.net (#45676) 2024-08-12 02:24:32 -06:00
Harmen Stoppels
7ddc886d6d buildcache: fix hard-coded, outdated layout version (#45645) 2024-08-12 09:25:31 +02:00
Wouter Deconinck
9e7183fb14 catch2: add v3.5.4, v3.6.0 (#45662)
* catch2: add v3.5.4, v3.6.0

* [@spackbot] updating style on behalf of wdconinc

---------

Co-authored-by: wdconinc <wdconinc@users.noreply.github.com>
2024-08-11 21:00:42 -07:00
Wouter Deconinck
18ab3c20ce opencascade: add v7.8.1 (#45665)
* opencascade: add v7.8.1

* opencascade: with default_args
2024-08-11 16:27:34 -06:00
Jim Phillips
b91b42dc7b namd: do not require single_node_gpu with rocm (#45650)
Removes conflict inadvertently left in #45553
2024-08-11 16:04:56 -06:00
Adam J. Stewart
7900d0b3db py-ruff: add v0.5.7 (#45660) 2024-08-11 14:31:23 -07:00
Wouter Deconinck
847d7bc87d libdrm: add v2.4.121, v2.4.122 (switch to multiple build systems) (#45663)
* libdrm: add  v2.4.121, v2.4.122 (switch to multiple build systems)

* [@spackbot] updating style on behalf of wdconinc

---------

Co-authored-by: wdconinc <wdconinc@users.noreply.github.com>
2024-08-11 14:26:37 -07:00
Wouter Deconinck
078984dcf4 libx11: add v1.8.10 (#45664) 2024-08-11 14:20:24 -07:00
Wouter Deconinck
010324714f py-gssapi: add v1.8.3 (#45666) 2024-08-11 14:09:45 -07:00
AcriusWinter
7ce5ac1e6e fortran: new test API (#45470)
* fortran: new test API
* fortran: add provides to gcc package
* fortran: simplify stand-alone test processing

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-11 14:32:39 -06:00
Steven Hahn
565165f02d benchmark: add 1.8.5 (#45657)
Signed-off-by: Steven Hahn <hahnse@ornl.gov>
2024-08-11 13:48:05 -06:00
AcriusWinter
e4869cd558 hypre-cmake: old to new test API (#45144)
* hypre-cmake: old to new test API
* hypre-cmake: update Makefile to use installed files
* hypre-cmake: make stand-alone test method name more specific

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-11 03:03:24 -06:00
AcriusWinter
990e0dc526 hypre: old to new test API (#45066)
* hypre: old to new test API
* hypre: restore test_parts; add Makefile cleanup

---------

Co-authored-by: Tamara Dahlgren <dahlgren1@llnl.gov>
2024-08-11 02:28:25 -06:00
Piotr Luszczek
f9d8b6b5aa plasma: add version 24.8.7 (#45656) 2024-08-11 02:14:21 -06:00
Massimiliano Culpo
2079b888c8 Remove the old concretizer (#45215)
The old concretizer is still used to bootstrap clingo from source. If we switch to a DAG model
where compilers are treated as nodes, we need to either:

1. fix the old concretizer to support this (which is a lot of work and possibly research), or
2. bootstrap `clingo` without the old concretizer.

This PR takes the second approach and gets rid of the old concretizer code. To bootstrap
`clingo`, we store some concrete spec prototypes as JSON, select one according to the
coarse-grained system architecture, and tweak them according to the current host.

The old concretizer and related dead code are removed.  In particular, this removes
`Spec.normalize()` and related methods, which were used in many unit-tests to set
up the test context. The tests have been updated not to use `normalize()`.

- [x] Bootstrap clingo concretization based on a JSON file
- [x] Bootstrap clingo *before* patchelf
- [x] Remove any use of the old concretizer, including:
      * Remove only_clingo and only_original fixtures
      * Remove _old_concretize and _new_concretize
      * Remove _concretize_together_old
      * Remove _concretize_together_new
      * Remove any use of `SPACK_TEST_SOLVER`
      * Simplify CI jobs
- [x] ensure bootstrapping `clingo` works on on Darwin and Windows
- [x] Raise an intelligible error when a compiler is missing
- [x] Ensure bootstrapping works on FreeBSD
- [x] remove normalize and related methods

Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2024-08-10 16:12:27 -07:00
1037 changed files with 16288 additions and 13307 deletions

View File

@@ -1,4 +1,5 @@
{
"name": "Ubuntu 20.04",
"image": "ghcr.io/spack/ubuntu20.04-runner-amd64-gcc-11.4:2023.08.01",
"postCreateCommand": "./.devcontainer/postCreateCommand.sh"
}

View File

@@ -0,0 +1,5 @@
{
"name": "Ubuntu 22.04",
"image": "ghcr.io/spack/ubuntu-22.04:v2024-05-07",
"postCreateCommand": "./.devcontainer/postCreateCommand.sh"
}

View File

@@ -29,7 +29,7 @@ jobs:
shell: ${{ matrix.system.shell }}
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: ${{inputs.python_version}}
- name: Install Python packages

View File

@@ -63,7 +63,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: "3.12"
- name: Bootstrap clingo
@@ -112,10 +112,10 @@ jobs:
runs-on: ${{ matrix.runner }}
strategy:
matrix:
runner: ['macos-13', 'macos-14', "ubuntu-latest"]
runner: ['macos-13', 'macos-14', "ubuntu-latest", "windows-latest"]
steps:
- name: Setup macOS
if: ${{ matrix.runner != 'ubuntu-latest' }}
if: ${{ matrix.runner != 'ubuntu-latest' && matrix.runner != 'windows-latest'}}
run: |
brew install tree
# Remove GnuPG since we want to bootstrap it
@@ -124,11 +124,16 @@ jobs:
if: ${{ matrix.runner == 'ubuntu-latest' }}
run: |
sudo rm -rf $(which gpg) $(which gpg2) $(which patchelf)
- name: Setup Windows
if: ${{ matrix.runner == 'windows-latest' }}
run: |
Remove-Item -Path (Get-Command gpg).Path
Remove-Item -Path (Get-Command file).Path
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: |
3.8
@@ -137,11 +142,20 @@ jobs:
3.11
3.12
- name: Set bootstrap sources
env:
SETUP_SCRIPT_EXT: ${{ matrix.runner == 'windows-latest' && 'ps1' || 'sh' }}
SETUP_SCRIPT_SOURCE: ${{ matrix.runner == 'windows-latest' && './' || 'source ' }}
run: |
${{ env.SETUP_SCRIPT_SOURCE }}share/spack/setup-env.${{ env.SETUP_SCRIPT_EXT }}
spack bootstrap disable github-actions-v0.4
- name: Disable from source bootstrap
if: ${{ matrix.runner != 'windows-latest' }}
run: |
source share/spack/setup-env.sh
spack bootstrap disable github-actions-v0.4
spack bootstrap disable spack-install
- name: Bootstrap clingo
# No binary clingo on Windows yet
if: ${{ matrix.runner != 'windows-latest' }}
run: |
set -e
for ver in '3.8' '3.9' '3.10' '3.11' '3.12' ; do
@@ -164,7 +178,24 @@ jobs:
fi
done
- name: Bootstrap GnuPG
env:
SETUP_SCRIPT_EXT: ${{ matrix.runner == 'windows-latest' && 'ps1' || 'sh' }}
SETUP_SCRIPT_SOURCE: ${{ matrix.runner == 'windows-latest' && './' || 'source ' }}
USER_SCOPE_PARENT_DIR: ${{ matrix.runner == 'windows-latest' && '$env:userprofile' || '$HOME' }}
VALIDATE_LAST_EXIT: ${{ matrix.runner == 'windows-latest' && './share/spack/qa/validate_last_exit.ps1' || '' }}
run: |
source share/spack/setup-env.sh
${{ env.SETUP_SCRIPT_SOURCE }}share/spack/setup-env.${{ env.SETUP_SCRIPT_EXT }}
spack -d gpg list
tree ~/.spack/bootstrap/store/
${{ env.VALIDATE_LAST_EXIT }}
tree ${{ env.USER_SCOPE_PARENT_DIR }}/.spack/bootstrap/store/
- name: Bootstrap File
env:
SETUP_SCRIPT_EXT: ${{ matrix.runner == 'windows-latest' && 'ps1' || 'sh' }}
SETUP_SCRIPT_SOURCE: ${{ matrix.runner == 'windows-latest' && './' || 'source ' }}
USER_SCOPE_PARENT_DIR: ${{ matrix.runner == 'windows-latest' && '$env:userprofile' || '$HOME' }}
VALIDATE_LAST_EXIT: ${{ matrix.runner == 'windows-latest' && './share/spack/qa/validate_last_exit.ps1' || '' }}
run: |
${{ env.SETUP_SCRIPT_SOURCE }}share/spack/setup-env.${{ env.SETUP_SCRIPT_EXT }}
spack -d python share/spack/qa/bootstrap-file.py
${{ env.VALIDATE_LAST_EXIT }}
tree ${{ env.USER_SCOPE_PARENT_DIR }}/.spack/bootstrap/store/

View File

@@ -87,7 +87,7 @@ jobs:
fi
- name: Upload Dockerfile
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874
with:
name: dockerfiles_${{ matrix.dockerfile[0] }}
path: dockerfiles
@@ -113,7 +113,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build & Deploy ${{ matrix.dockerfile[0] }}
uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85
with:
context: dockerfiles/${{ matrix.dockerfile[0] }}
platforms: ${{ matrix.dockerfile[1] }}
@@ -126,7 +126,7 @@ jobs:
needs: deploy-images
steps:
- name: Merge Artifacts
uses: actions/upload-artifact/merge@834a144ee995460fba8ed112a2fc961b36a5ec5a
uses: actions/upload-artifact/merge@50769540e7f4bd5e21e526ee35c689e35e0d6874
with:
name: dockerfiles
pattern: dockerfiles_*

View File

@@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: 3.9
- name: Install Python packages

View File

@@ -16,45 +16,34 @@ jobs:
matrix:
os: [ubuntu-latest]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
concretizer: ['clingo']
on_develop:
- ${{ github.ref == 'refs/heads/develop' }}
include:
- python-version: '3.11'
os: ubuntu-latest
concretizer: original
on_develop: ${{ github.ref == 'refs/heads/develop' }}
- python-version: '3.6'
os: ubuntu-20.04
concretizer: clingo
on_develop: ${{ github.ref == 'refs/heads/develop' }}
exclude:
- python-version: '3.7'
os: ubuntu-latest
concretizer: 'clingo'
on_develop: false
- python-version: '3.8'
os: ubuntu-latest
concretizer: 'clingo'
on_develop: false
- python-version: '3.9'
os: ubuntu-latest
concretizer: 'clingo'
on_develop: false
- python-version: '3.10'
os: ubuntu-latest
concretizer: 'clingo'
on_develop: false
- python-version: '3.11'
os: ubuntu-latest
concretizer: 'clingo'
on_develop: false
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: ${{ matrix.python-version }}
- name: Install System packages
@@ -85,7 +74,6 @@ jobs:
- name: Run unit tests
env:
SPACK_PYTHON: python
SPACK_TEST_SOLVER: ${{ matrix.concretizer }}
SPACK_TEST_PARALLEL: 2
COVERAGE: true
UNIT_TEST_COVERAGE: ${{ matrix.python-version == '3.11' }}
@@ -103,7 +91,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: '3.11'
- name: Install System packages
@@ -163,7 +151,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: '3.11'
- name: Install System packages
@@ -182,7 +170,6 @@ jobs:
- name: Run unit tests (full suite with coverage)
env:
COVERAGE: true
SPACK_TEST_SOLVER: clingo
run: |
share/spack/qa/run-unit-tests
- uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673
@@ -201,7 +188,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: ${{ matrix.python-version }}
- name: Install Python packages
@@ -213,7 +200,6 @@ jobs:
brew install dash fish gcc gnupg2 kcov
- name: Run unit tests
env:
SPACK_TEST_SOLVER: clingo
SPACK_TEST_PARALLEL: 4
run: |
git --version
@@ -239,7 +225,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: 3.9
- name: Install Python packages

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: '3.11'
cache: 'pip'
@@ -38,7 +38,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
with:
python-version: '3.11'
cache: 'pip'

View File

@@ -115,12 +115,6 @@ config:
suppress_gpg_warnings: false
# If set to true, Spack will attempt to build any compiler on the spec
# that is not already available. If set to False, Spack will only use
# compilers already configured in compilers.yaml
install_missing_compilers: false
# If set to true, Spack will always check checksums after downloading
# archives. If false, Spack skips the checksum step.
checksum: true
@@ -170,23 +164,6 @@ config:
# If set to true, Spack will use ccache to cache C compiles.
ccache: false
# The concretization algorithm to use in Spack. Options are:
#
# 'clingo': Uses a logic solver under the hood to solve DAGs with full
# backtracking and optimization for user preferences. Spack will
# try to bootstrap the logic solver, if not already available.
#
# 'original': Spack's original greedy, fixed-point concretizer. This
# algorithm can make decisions too early and will not backtrack
# sufficiently for many specs. This will soon be deprecated in
# favor of clingo.
#
# See `concretizer.yaml` for more settings you can fine-tune when
# using clingo.
concretizer: clingo
# How long to wait to lock the Spack installation database. This lock is used
# when Spack needs to manage its own package metadata and all operations are
# expected to complete within the default time limit. The timeout should

View File

@@ -20,12 +20,14 @@ packages:
awk: [gawk]
armci: [armcimpi]
blas: [openblas, amdblis]
c: [gcc]
cxx: [gcc]
D: [ldc]
daal: [intel-oneapi-daal]
elf: [elfutils]
fftw-api: [fftw, amdfftw]
flame: [libflame, amdlibflame]
fortran: [gcc]
fortran-rt: [gcc-runtime, intel-oneapi-runtime]
fuse: [libfuse]
gl: [glx, osmesa]
@@ -70,3 +72,13 @@ packages:
permissions:
read: world
write: user
cray-mpich:
buildable: false
cray-mvapich2:
buildable: false
fujitsu-mpi:
buildable: false
hpcx-mpi:
buildable: false
spectrum-mpi:
buildable: false

View File

@@ -1,6 +1,5 @@
config:
locks: false
concretizer: clingo
build_stage::
- '$spack/.staging'
stage_name: '{name}-{version}-{hash:7}'

View File

@@ -206,6 +206,7 @@ def setup(sphinx):
("py:class", "six.moves.urllib.parse.ParseResult"),
("py:class", "TextIO"),
("py:class", "hashlib._Hash"),
("py:class", "concurrent.futures._base.Executor"),
# Spack classes that are private and we don't want to expose
("py:class", "spack.provider_index._IndexBase"),
("py:class", "spack.repo._PrependFileLoader"),
@@ -217,6 +218,7 @@ def setup(sphinx):
("py:class", "spack.spec.SpecfileReaderBase"),
("py:class", "spack.install_test.Pb"),
("py:class", "spack.filesystem_view.SimpleFilesystemView"),
("py:class", "spack.traverse.EdgeAndDepth"),
]
# The reST default role (used for this markup: `text`) to use for all documents.

View File

@@ -181,10 +181,6 @@ Spec-related modules
:mod:`spack.parser`
Contains :class:`~spack.parser.SpecParser` and functions related to parsing specs.
:mod:`spack.concretize`
Contains :class:`~spack.concretize.Concretizer` implementation,
which allows site administrators to change Spack's :ref:`concretization-policies`.
:mod:`spack.version`
Implements a simple :class:`~spack.version.Version` class with simple
comparison semantics. Also implements :class:`~spack.version.VersionRange`

View File

@@ -863,7 +863,7 @@ named list ``compilers`` is ``['%gcc', '%clang', '%intel']`` on
spack:
definitions:
- compilers: ['%gcc', '%clang']
- when: arch.satisfies('x86_64:')
- when: arch.satisfies('target=x86_64:')
compilers: ['%intel']
.. note::
@@ -893,8 +893,9 @@ The valid variables for a ``when`` clause are:
#. ``env``. The user environment (usually ``os.environ`` in Python).
#. ``hostname``. The hostname of the system (if ``hostname`` is an
executable in the user's PATH).
#. ``hostname``. The hostname of the system.
#. ``full_hostname``. The fully qualified hostname of the system.
^^^^^^^^^^^^^^^^^^^^^^^^
SpecLists as Constraints

View File

@@ -1263,6 +1263,11 @@ Git fetching supports the following parameters to ``version``:
option ``--depth 1`` will be used if the version of git and the specified
transport protocol support it, and ``--single-branch`` will be used if the
version of git supports it.
* ``git_sparse_paths``: Use ``sparse-checkout`` to only clone these relative paths.
This feature requires ``git`` to be version ``2.25.0`` or later but is useful for
large repositories that have separate portions that can be built independently.
If paths provided are directories then all the subdirectories and associated files
will also be cloned.
Only one of ``tag``, ``branch``, or ``commit`` can be used at a time.
@@ -1361,6 +1366,41 @@ Submodules
For more information about git submodules see the manpage of git: ``man
git-submodule``.
Sparse-Checkout
You can supply ``git_sparse_paths`` at the package or version level to utilize git's
sparse-checkout feature. This will only clone the paths that are specified in the
``git_sparse_paths`` attribute for the package along with the files in the top level directory.
This feature allows you to only clone what you need from a large repository.
Note that this is a newer feature in git and requries git ``2.25.0`` or greater.
If ``git_sparse_paths`` is supplied and the git version is too old
then a warning will be issued and that package will use the standard cloning operations instead.
``git_sparse_paths`` should be supplied as a list of paths, a callable function for versions,
or a more complex package attribute using the ``@property`` decorator. The return value should be
a list for a callable implementation of ``git_sparse_paths``.
.. code-block:: python
def sparse_path_function(package)
"""a callable function that can be used in side a version"""
# paths can be directories or functions, all subdirectories and files are included
paths = ["doe", "rae", "me/file.cpp"]
if package.spec.version > Version("1.2.0"):
paths.extend(["fae"])
return paths
class MyPackage(package):
# can also be a package attribute that will be used if not specified in versions
git_sparse_paths = ["doe", "rae"]
# use the package attribute
version("1.0.0")
version("1.1.0")
# use the function
version("1.1.5", git_sparse_paths=sparse_path_func)
version("1.2.0", git_sparse_paths=sparse_path_func)
version("1.2.5", git_sparse_paths=sparse_path_func)
version("1.1.5", git_sparse_paths=sparse_path_func)
.. _github-fetch:
^^^^^^

View File

@@ -663,11 +663,7 @@ build the package.
When including a bootstrapping phase as in the example above, the result is that
the bootstrapped compiler packages will be pushed to the binary mirror (and the
local artifacts mirror) before the actual release specs are built. In this case,
the jobs corresponding to subsequent release specs are configured to
``install_missing_compilers``, so that if spack is asked to install a package
with a compiler it doesn't know about, it can be quickly installed from the
binary mirror first.
local artifacts mirror) before the actual release specs are built.
Since bootstrapping compilers is optional, those items can be left out of the
environment/stack file, and in that case no bootstrapping will be done (only the

View File

@@ -5,8 +5,8 @@ sphinx-rtd-theme==2.0.0
python-levenshtein==0.25.1
docutils==0.20.1
pygments==2.18.0
urllib3==2.2.2
pytest==8.3.2
urllib3==2.2.3
pytest==8.3.3
isort==5.13.2
black==24.8.0
flake8==7.1.1

View File

@@ -18,7 +18,7 @@
* Homepage: https://pypi.python.org/pypi/archspec
* Usage: Labeling, comparison and detection of microarchitectures
* Version: 0.2.4 (commit 48b92512b9ce203ded0ebd1ac41b42593e931f7c)
* Version: 0.2.5-dev (commit 7e6740012b897ae4a950f0bba7e9726b767e921f)
astunparse
----------------

View File

@@ -1265,27 +1265,29 @@ def _distro_release_info(self) -> Dict[str, str]:
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
else:
try:
basenames = [
basename
for basename in os.listdir(self.etc_dir)
if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES
and os.path.isfile(os.path.join(self.etc_dir, basename))
]
with os.scandir(self.etc_dir) as it:
etc_files = [
p.path for p in it
if p.is_file() and p.name not in _DISTRO_RELEASE_IGNORE_BASENAMES
]
# We sort for repeatability in cases where there are multiple
# distro specific files; e.g. CentOS, Oracle, Enterprise all
# containing `redhat-release` on top of their own.
basenames.sort()
etc_files.sort()
except OSError:
# This may occur when /etc is not readable but we can't be
# sure about the *-release files. Check common entries of
# /etc for information. If they turn out to not be there the
# error is handled in `_parse_distro_release_file()`.
basenames = _DISTRO_RELEASE_BASENAMES
for basename in basenames:
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
etc_files = [
os.path.join(self.etc_dir, basename)
for basename in _DISTRO_RELEASE_BASENAMES
]
for filepath in etc_files:
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(os.path.basename(filepath))
if match is None:
continue
filepath = os.path.join(self.etc_dir, basename)
distro_info = self._parse_distro_release_file(filepath)
# The name is always present if the pattern matches.
if "name" not in distro_info:

View File

@@ -231,96 +231,6 @@ def is_host_name(instance):
return True
try:
# The built-in `idna` codec only implements RFC 3890, so we go elsewhere.
import idna
except ImportError:
pass
else:
@_checks_drafts(draft7="idn-hostname", raises=idna.IDNAError)
def is_idn_host_name(instance):
if not isinstance(instance, str_types):
return True
idna.encode(instance)
return True
try:
import rfc3987
except ImportError:
try:
from rfc3986_validator import validate_rfc3986
except ImportError:
pass
else:
@_checks_drafts(name="uri")
def is_uri(instance):
if not isinstance(instance, str_types):
return True
return validate_rfc3986(instance, rule="URI")
@_checks_drafts(
draft6="uri-reference",
draft7="uri-reference",
raises=ValueError,
)
def is_uri_reference(instance):
if not isinstance(instance, str_types):
return True
return validate_rfc3986(instance, rule="URI_reference")
else:
@_checks_drafts(draft7="iri", raises=ValueError)
def is_iri(instance):
if not isinstance(instance, str_types):
return True
return rfc3987.parse(instance, rule="IRI")
@_checks_drafts(draft7="iri-reference", raises=ValueError)
def is_iri_reference(instance):
if not isinstance(instance, str_types):
return True
return rfc3987.parse(instance, rule="IRI_reference")
@_checks_drafts(name="uri", raises=ValueError)
def is_uri(instance):
if not isinstance(instance, str_types):
return True
return rfc3987.parse(instance, rule="URI")
@_checks_drafts(
draft6="uri-reference",
draft7="uri-reference",
raises=ValueError,
)
def is_uri_reference(instance):
if not isinstance(instance, str_types):
return True
return rfc3987.parse(instance, rule="URI_reference")
try:
from strict_rfc3339 import validate_rfc3339
except ImportError:
try:
from rfc3339_validator import validate_rfc3339
except ImportError:
validate_rfc3339 = None
if validate_rfc3339:
@_checks_drafts(name="date-time")
def is_datetime(instance):
if not isinstance(instance, str_types):
return True
return validate_rfc3339(instance)
@_checks_drafts(draft7="time")
def is_time(instance):
if not isinstance(instance, str_types):
return True
return is_datetime("1970-01-01T" + instance)
@_checks_drafts(name="regex", raises=re.error)
def is_regex(instance):
if not isinstance(instance, str_types):
@@ -340,86 +250,3 @@ def is_draft3_time(instance):
if not isinstance(instance, str_types):
return True
return datetime.datetime.strptime(instance, "%H:%M:%S")
try:
import webcolors
except ImportError:
pass
else:
def is_css_color_code(instance):
return webcolors.normalize_hex(instance)
@_checks_drafts(draft3="color", raises=(ValueError, TypeError))
def is_css21_color(instance):
if (
not isinstance(instance, str_types) or
instance.lower() in webcolors.css21_names_to_hex
):
return True
return is_css_color_code(instance)
def is_css3_color(instance):
if instance.lower() in webcolors.css3_names_to_hex:
return True
return is_css_color_code(instance)
try:
import jsonpointer
except ImportError:
pass
else:
@_checks_drafts(
draft6="json-pointer",
draft7="json-pointer",
raises=jsonpointer.JsonPointerException,
)
def is_json_pointer(instance):
if not isinstance(instance, str_types):
return True
return jsonpointer.JsonPointer(instance)
# TODO: I don't want to maintain this, so it
# needs to go either into jsonpointer (pending
# https://github.com/stefankoegl/python-json-pointer/issues/34) or
# into a new external library.
@_checks_drafts(
draft7="relative-json-pointer",
raises=jsonpointer.JsonPointerException,
)
def is_relative_json_pointer(instance):
# Definition taken from:
# https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3
if not isinstance(instance, str_types):
return True
non_negative_integer, rest = [], ""
for i, character in enumerate(instance):
if character.isdigit():
non_negative_integer.append(character)
continue
if not non_negative_integer:
return False
rest = instance[i:]
break
return (rest == "#") or jsonpointer.JsonPointer(rest)
try:
import uritemplate.exceptions
except ImportError:
pass
else:
@_checks_drafts(
draft6="uri-template",
draft7="uri-template",
raises=uritemplate.exceptions.InvalidTemplate,
)
def is_uri_template(
instance,
template_validator=uritemplate.Validator().force_balanced_braces(),
):
template = uritemplate.URITemplate(instance)
return template_validator.validate(template)

View File

@@ -47,7 +47,11 @@ def decorator(factory):
def partial_uarch(
name: str = "", vendor: str = "", features: Optional[Set[str]] = None, generation: int = 0
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(
@@ -57,6 +61,7 @@ def partial_uarch(
features=features or set(),
compilers={},
generation=generation,
cpu_part=cpu_part,
)
@@ -90,6 +95,7 @@ def proc_cpuinfo() -> Microarchitecture:
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):
@@ -345,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

View File

@@ -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
@@ -65,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
@@ -111,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
@@ -143,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)
@@ -190,6 +193,7 @@ def to_dict(self):
"generation": self.generation,
"parents": [str(x) for x in self.parents],
"compilers": self.compilers,
"cpupart": self.cpu_part,
}
@staticmethod
@@ -202,6 +206,7 @@ def from_dict(data) -> "Microarchitecture":
features=set(data["features"]),
compilers=data.get("compilers", {}),
generation=data.get("generation", 0),
cpu_part=data.get("cpupart", ""),
)
def optimization_flags(self, compiler, version):
@@ -360,8 +365,11 @@ 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)
targets[name] = Microarchitecture(
name, parents, vendor, features, compilers, generation=generation, cpu_part=cpu_part
)
known_targets = {}
data = archspec.cpu.schema.TARGETS_JSON["microarchitectures"]

View File

@@ -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"],
@@ -2902,7 +2909,8 @@
"flags": "-tp {name}"
}
]
}
},
"cpupart": "0xd0c"
},
"neoverse_v1": {
"from": ["neoverse_n1", "armv8.4a"],
@@ -2926,8 +2934,6 @@
"lrcpc",
"dcpop",
"sha3",
"sm3",
"sm4",
"asimddp",
"sha512",
"sve",
@@ -3028,7 +3034,8 @@
"flags": "-tp {name}"
}
]
}
},
"cpupart": "0xd40"
},
"neoverse_v2": {
"from": ["neoverse_n1", "armv9.0a"],
@@ -3052,13 +3059,10 @@
"lrcpc",
"dcpop",
"sha3",
"sm3",
"sm4",
"asimddp",
"sha512",
"sve",
"asimdfhm",
"dit",
"uscat",
"ilrcpc",
"flagm",
@@ -3066,18 +3070,12 @@
"sb",
"dcpodp",
"sve2",
"sveaes",
"svepmull",
"svebitperm",
"svesha3",
"svesm4",
"flagm2",
"frint",
"svei8mm",
"svebf16",
"i8mm",
"bf16",
"dgh"
"bf16"
],
"compilers" : {
"gcc": [
@@ -3102,15 +3100,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"
}
],
@@ -3145,7 +3147,113 @@
"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",
"ssbs",
"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"],
@@ -3211,7 +3319,8 @@
"flags" : "-mcpu=apple-m1"
}
]
}
},
"cpupart": "0x022"
},
"m2": {
"from": ["m1", "armv8.5a"],
@@ -3289,7 +3398,8 @@
"flags" : "-mcpu=apple-m2"
}
]
}
},
"cpupart": "0x032"
},
"arm": {
"from": [],

View File

@@ -52,6 +52,9 @@
}
}
}
},
"cpupart": {
"type": "string"
}
},
"required": [
@@ -107,4 +110,4 @@
"additionalProperties": false
}
}
}
}

45
lib/spack/external/patches/distro.patch vendored Normal file
View File

@@ -0,0 +1,45 @@
diff --git a/lib/spack/external/_vendoring/distro/distro.py b/lib/spack/external/_vendoring/distro/distro.py
index 89e1868047..50c3b18d4d 100644
--- a/lib/spack/external/_vendoring/distro/distro.py
+++ b/lib/spack/external/_vendoring/distro/distro.py
@@ -1265,27 +1265,29 @@ def _distro_release_info(self) -> Dict[str, str]:
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
else:
try:
- basenames = [
- basename
- for basename in os.listdir(self.etc_dir)
- if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES
- and os.path.isfile(os.path.join(self.etc_dir, basename))
- ]
+ with os.scandir(self.etc_dir) as it:
+ etc_files = [
+ p.path for p in it
+ if p.is_file() and p.name not in _DISTRO_RELEASE_IGNORE_BASENAMES
+ ]
# We sort for repeatability in cases where there are multiple
# distro specific files; e.g. CentOS, Oracle, Enterprise all
# containing `redhat-release` on top of their own.
- basenames.sort()
+ etc_files.sort()
except OSError:
# This may occur when /etc is not readable but we can't be
# sure about the *-release files. Check common entries of
# /etc for information. If they turn out to not be there the
# error is handled in `_parse_distro_release_file()`.
- basenames = _DISTRO_RELEASE_BASENAMES
- for basename in basenames:
- match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
+ etc_files = [
+ os.path.join(self.etc_dir, basename)
+ for basename in _DISTRO_RELEASE_BASENAMES
+ ]
+
+ for filepath in etc_files:
+ match = _DISTRO_RELEASE_BASENAME_PATTERN.match(os.path.basename(filepath))
if match is None:
continue
- filepath = os.path.join(self.etc_dir, basename)
distro_info = self._parse_distro_release_file(filepath)
# The name is always present if the pattern matches.
if "name" not in distro_info:

View File

@@ -13,3 +13,191 @@ index 6b630cdfbb..1791fe7fbf 100644
-__version__ = metadata.version("jsonschema")
+
+__version__ = "3.2.0"
diff --git a/lib/spack/external/_vendoring/jsonschema/_format.py b/lib/spack/external/_vendoring/jsonschema/_format.py
index 281a7cfcff..29061e3661 100644
--- a/lib/spack/external/_vendoring/jsonschema/_format.py
+++ b/lib/spack/external/_vendoring/jsonschema/_format.py
@@ -231,96 +231,6 @@ def is_host_name(instance):
return True
-try:
- # The built-in `idna` codec only implements RFC 3890, so we go elsewhere.
- import idna
-except ImportError:
- pass
-else:
- @_checks_drafts(draft7="idn-hostname", raises=idna.IDNAError)
- def is_idn_host_name(instance):
- if not isinstance(instance, str_types):
- return True
- idna.encode(instance)
- return True
-
-
-try:
- import rfc3987
-except ImportError:
- try:
- from rfc3986_validator import validate_rfc3986
- except ImportError:
- pass
- else:
- @_checks_drafts(name="uri")
- def is_uri(instance):
- if not isinstance(instance, str_types):
- return True
- return validate_rfc3986(instance, rule="URI")
-
- @_checks_drafts(
- draft6="uri-reference",
- draft7="uri-reference",
- raises=ValueError,
- )
- def is_uri_reference(instance):
- if not isinstance(instance, str_types):
- return True
- return validate_rfc3986(instance, rule="URI_reference")
-
-else:
- @_checks_drafts(draft7="iri", raises=ValueError)
- def is_iri(instance):
- if not isinstance(instance, str_types):
- return True
- return rfc3987.parse(instance, rule="IRI")
-
- @_checks_drafts(draft7="iri-reference", raises=ValueError)
- def is_iri_reference(instance):
- if not isinstance(instance, str_types):
- return True
- return rfc3987.parse(instance, rule="IRI_reference")
-
- @_checks_drafts(name="uri", raises=ValueError)
- def is_uri(instance):
- if not isinstance(instance, str_types):
- return True
- return rfc3987.parse(instance, rule="URI")
-
- @_checks_drafts(
- draft6="uri-reference",
- draft7="uri-reference",
- raises=ValueError,
- )
- def is_uri_reference(instance):
- if not isinstance(instance, str_types):
- return True
- return rfc3987.parse(instance, rule="URI_reference")
-
-
-try:
- from strict_rfc3339 import validate_rfc3339
-except ImportError:
- try:
- from rfc3339_validator import validate_rfc3339
- except ImportError:
- validate_rfc3339 = None
-
-if validate_rfc3339:
- @_checks_drafts(name="date-time")
- def is_datetime(instance):
- if not isinstance(instance, str_types):
- return True
- return validate_rfc3339(instance)
-
- @_checks_drafts(draft7="time")
- def is_time(instance):
- if not isinstance(instance, str_types):
- return True
- return is_datetime("1970-01-01T" + instance)
-
-
@_checks_drafts(name="regex", raises=re.error)
def is_regex(instance):
if not isinstance(instance, str_types):
@@ -340,86 +250,3 @@ def is_draft3_time(instance):
if not isinstance(instance, str_types):
return True
return datetime.datetime.strptime(instance, "%H:%M:%S")
-
-
-try:
- import webcolors
-except ImportError:
- pass
-else:
- def is_css_color_code(instance):
- return webcolors.normalize_hex(instance)
-
- @_checks_drafts(draft3="color", raises=(ValueError, TypeError))
- def is_css21_color(instance):
- if (
- not isinstance(instance, str_types) or
- instance.lower() in webcolors.css21_names_to_hex
- ):
- return True
- return is_css_color_code(instance)
-
- def is_css3_color(instance):
- if instance.lower() in webcolors.css3_names_to_hex:
- return True
- return is_css_color_code(instance)
-
-
-try:
- import jsonpointer
-except ImportError:
- pass
-else:
- @_checks_drafts(
- draft6="json-pointer",
- draft7="json-pointer",
- raises=jsonpointer.JsonPointerException,
- )
- def is_json_pointer(instance):
- if not isinstance(instance, str_types):
- return True
- return jsonpointer.JsonPointer(instance)
-
- # TODO: I don't want to maintain this, so it
- # needs to go either into jsonpointer (pending
- # https://github.com/stefankoegl/python-json-pointer/issues/34) or
- # into a new external library.
- @_checks_drafts(
- draft7="relative-json-pointer",
- raises=jsonpointer.JsonPointerException,
- )
- def is_relative_json_pointer(instance):
- # Definition taken from:
- # https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3
- if not isinstance(instance, str_types):
- return True
- non_negative_integer, rest = [], ""
- for i, character in enumerate(instance):
- if character.isdigit():
- non_negative_integer.append(character)
- continue
-
- if not non_negative_integer:
- return False
-
- rest = instance[i:]
- break
- return (rest == "#") or jsonpointer.JsonPointer(rest)
-
-
-try:
- import uritemplate.exceptions
-except ImportError:
- pass
-else:
- @_checks_drafts(
- draft6="uri-template",
- draft7="uri-template",
- raises=uritemplate.exceptions.InvalidTemplate,
- )
- def is_uri_template(
- instance,
- template_validator=uritemplate.Validator().force_balanced_braces(),
- ):
- template = uritemplate.URITemplate(instance)
- return template_validator.validate(template)

View File

@@ -27,8 +27,6 @@
from llnl.util.lang import dedupe, memoized
from llnl.util.symlink import islink, readlink, resolve_link_target_relative_to_the_link, symlink
from spack.util.executable import Executable, which
from ..path import path_to_os_path, system_path_filter
if sys.platform != "win32":
@@ -53,7 +51,6 @@
"find_all_headers",
"find_libraries",
"find_system_libraries",
"fix_darwin_install_name",
"force_remove",
"force_symlink",
"getuid",
@@ -248,42 +245,6 @@ def path_contains_subdirectory(path, root):
return norm_path.startswith(norm_root)
@memoized
def file_command(*args):
"""Creates entry point to `file` system command with provided arguments"""
file_cmd = which("file", required=True)
for arg in args:
file_cmd.add_default_arg(arg)
return file_cmd
@memoized
def _get_mime_type():
"""Generate method to call `file` system command to aquire mime type
for a specified path
"""
if sys.platform == "win32":
# -h option (no-dereference) does not exist in Windows
return file_command("-b", "--mime-type")
else:
return file_command("-b", "-h", "--mime-type")
def mime_type(filename):
"""Returns the mime type and subtype of a file.
Args:
filename: file to be analyzed
Returns:
Tuple containing the MIME type and subtype
"""
output = _get_mime_type()(filename, output=str, error=str).strip()
tty.debug("==> " + output)
type, _, subtype = output.partition("/")
return type, subtype
#: This generates the library filenames that may appear on any OS.
library_extensions = ["a", "la", "so", "tbd", "dylib"]
@@ -1624,6 +1585,12 @@ def remove_linked_tree(path):
shutil.rmtree(os.path.realpath(path), **kwargs)
os.unlink(path)
else:
if sys.platform == "win32":
# Adding this prefix allows shutil to remove long paths on windows
# https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
long_path_pfx = "\\\\?\\"
if not path.startswith(long_path_pfx):
path = long_path_pfx + path
shutil.rmtree(path, **kwargs)
@@ -1673,41 +1640,6 @@ def safe_remove(*files_or_dirs):
raise
@system_path_filter
def fix_darwin_install_name(path):
"""Fix install name of dynamic libraries on Darwin to have full path.
There are two parts of this task:
1. Use ``install_name('-id', ...)`` to change install name of a single lib
2. Use ``install_name('-change', ...)`` to change the cross linking between
libs. The function assumes that all libraries are in one folder and
currently won't follow subfolders.
Parameters:
path (str): directory in which .dylib files are located
"""
libs = glob.glob(join_path(path, "*.dylib"))
for lib in libs:
# fix install name first:
install_name_tool = Executable("install_name_tool")
install_name_tool("-id", lib, lib)
otool = Executable("otool")
long_deps = otool("-L", lib, output=str).split("\n")
deps = [dep.partition(" ")[0][1::] for dep in long_deps[2:-1]]
# fix all dependencies:
for dep in deps:
for loc in libs:
# We really want to check for either
# dep == os.path.basename(loc) or
# dep == join_path(builddir, os.path.basename(loc)),
# but we don't know builddir (nor how symbolic links look
# in builddir). We thus only compare the basenames.
if os.path.basename(dep) == os.path.basename(loc):
install_name_tool("-change", dep, loc, lib)
break
def find_first(root: str, files: Union[Iterable[str], str], bfs_depth: int = 2) -> Optional[str]:
"""Find the first file matching a pattern.

View File

@@ -6,7 +6,6 @@
import collections.abc
import contextlib
import functools
import inspect
import itertools
import os
import re
@@ -16,7 +15,7 @@
from typing import Any, Callable, Iterable, List, Tuple
# Ignore emacs backups when listing modules
ignore_modules = [r"^\.#", "~$"]
ignore_modules = r"^\.#|~$"
def index_by(objects, *funcs):
@@ -84,20 +83,6 @@ def index_by(objects, *funcs):
return result
def caller_locals():
"""This will return the locals of the *parent* of the caller.
This allows a function to insert variables into its caller's
scope. Yes, this is some black magic, and yes it's useful
for implementing things like depends_on and provides.
"""
# Passing zero here skips line context for speed.
stack = inspect.stack(0)
try:
return stack[2][0].f_locals
finally:
del stack
def attr_setdefault(obj, name, value):
"""Like dict.setdefault, but for objects."""
if not hasattr(obj, name):
@@ -105,15 +90,6 @@ def attr_setdefault(obj, name, value):
return getattr(obj, name)
def has_method(cls, name):
for base in inspect.getmro(cls):
if base is object:
continue
if name in base.__dict__:
return True
return False
def union_dicts(*dicts):
"""Use update() to combine all dicts into one.
@@ -178,19 +154,22 @@ def list_modules(directory, **kwargs):
order."""
list_directories = kwargs.setdefault("directories", True)
for name in os.listdir(directory):
if name == "__init__.py":
continue
ignore = re.compile(ignore_modules)
path = os.path.join(directory, name)
if list_directories and os.path.isdir(path):
init_py = os.path.join(path, "__init__.py")
if os.path.isfile(init_py):
yield name
with os.scandir(directory) as it:
for entry in it:
if entry.name == "__init__.py" or entry.name == "__pycache__":
continue
elif name.endswith(".py"):
if not any(re.search(pattern, name) for pattern in ignore_modules):
yield re.sub(".py$", "", name)
if (
list_directories
and entry.is_dir()
and os.path.isfile(os.path.join(entry.path, "__init__.py"))
):
yield entry.name
elif entry.name.endswith(".py") and entry.is_file() and not ignore.search(entry.name):
yield entry.name[:-3] # strip .py
def decorator_with_or_without_args(decorator):
@@ -237,8 +216,8 @@ def setter(name, value):
value.__name__ = name
setattr(cls, name, value)
if not has_method(cls, "_cmp_key"):
raise TypeError("'%s' doesn't define _cmp_key()." % cls.__name__)
if not hasattr(cls, "_cmp_key"):
raise TypeError(f"'{cls.__name__}' doesn't define _cmp_key().")
setter("__eq__", lambda s, o: (s is o) or (o is not None and s._cmp_key() == o._cmp_key()))
setter("__lt__", lambda s, o: o is not None and s._cmp_key() < o._cmp_key())
@@ -388,8 +367,8 @@ def cd_fun():
TypeError: If the class does not have a ``_cmp_iter`` method
"""
if not has_method(cls, "_cmp_iter"):
raise TypeError("'%s' doesn't define _cmp_iter()." % cls.__name__)
if not hasattr(cls, "_cmp_iter"):
raise TypeError(f"'{cls.__name__}' doesn't define _cmp_iter().")
# comparison operators are implemented in terms of lazy_eq and lazy_lt
def eq(self, other):
@@ -864,20 +843,19 @@ def uniq(sequence):
return uniq_list
def elide_list(line_list, max_num=10):
def elide_list(line_list: List[str], max_num: int = 10) -> List[str]:
"""Takes a long list and limits it to a smaller number of elements,
replacing intervening elements with '...'. For example::
elide_list([1,2,3,4,5,6], 4)
elide_list(["1", "2", "3", "4", "5", "6"], 4)
gives::
[1, 2, 3, '...', 6]
["1", "2", "3", "...", "6"]
"""
if len(line_list) > max_num:
return line_list[: max_num - 1] + ["..."] + line_list[-1:]
else:
return line_list
return [*line_list[: max_num - 1], "...", line_list[-1]]
return line_list
@contextlib.contextmanager

View File

@@ -10,6 +10,7 @@
import errno
import io
import multiprocessing
import multiprocessing.connection
import os
import re
import select

View File

@@ -1,131 +0,0 @@
# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
from llnl.util.lang import memoized
import spack.spec
import spack.version
from spack.compilers.clang import Clang
from spack.util.executable import Executable, ProcessError
class ABI:
"""This class provides methods to test ABI compatibility between specs.
The current implementation is rather rough and could be improved."""
def architecture_compatible(
self, target: spack.spec.Spec, constraint: spack.spec.Spec
) -> bool:
"""Return true if architecture of target spec is ABI compatible
to the architecture of constraint spec. If either the target
or constraint specs have no architecture, target is also defined
as architecture ABI compatible to constraint."""
return (
not target.architecture
or not constraint.architecture
or target.architecture.intersects(constraint.architecture)
)
@memoized
def _gcc_get_libstdcxx_version(self, version):
"""Returns gcc ABI compatibility info by getting the library version of
a compiler's libstdc++ or libgcc_s"""
from spack.build_environment import dso_suffix
spec = spack.spec.CompilerSpec("gcc", version)
compilers = spack.compilers.compilers_for_spec(spec)
if not compilers:
return None
compiler = compilers[0]
rungcc = None
libname = None
output = None
if compiler.cxx:
rungcc = Executable(compiler.cxx)
libname = "libstdc++." + dso_suffix
elif compiler.cc:
rungcc = Executable(compiler.cc)
libname = "libgcc_s." + dso_suffix
else:
return None
try:
# Some gcc's are actually clang and don't respond properly to
# --print-file-name (they just print the filename, not the
# full path). Ignore these and expect them to be handled as clang.
if Clang.default_version(rungcc.exe[0]) != "unknown":
return None
output = rungcc("--print-file-name=%s" % libname, output=str)
except ProcessError:
return None
if not output:
return None
libpath = os.path.realpath(output.strip())
if not libpath:
return None
return os.path.basename(libpath)
@memoized
def _gcc_compiler_compare(self, pversion, cversion):
"""Returns true iff the gcc version pversion and cversion
are ABI compatible."""
plib = self._gcc_get_libstdcxx_version(pversion)
clib = self._gcc_get_libstdcxx_version(cversion)
if not plib or not clib:
return False
return plib == clib
def _intel_compiler_compare(
self, pversion: spack.version.ClosedOpenRange, cversion: spack.version.ClosedOpenRange
) -> bool:
"""Returns true iff the intel version pversion and cversion
are ABI compatible"""
# Test major and minor versions. Ignore build version.
pv = pversion.lo
cv = cversion.lo
return pv.up_to(2) == cv.up_to(2)
def compiler_compatible(
self, parent: spack.spec.Spec, child: spack.spec.Spec, loose: bool = False
) -> bool:
"""Return true if compilers for parent and child are ABI compatible."""
if not parent.compiler or not child.compiler:
return True
if parent.compiler.name != child.compiler.name:
# Different compiler families are assumed ABI incompatible
return False
if loose:
return True
# TODO: Can we move the specialized ABI matching stuff
# TODO: into compiler classes?
for pversion in parent.compiler.versions:
for cversion in child.compiler.versions:
# For a few compilers use specialized comparisons.
# Otherwise match on version match.
if pversion.intersects(cversion):
return True
elif parent.compiler.name == "gcc" and self._gcc_compiler_compare(
pversion, cversion
):
return True
elif parent.compiler.name == "intel" and self._intel_compiler_compare(
pversion, cversion
):
return True
return False
def compatible(
self, target: spack.spec.Spec, constraint: spack.spec.Spec, loose: bool = False
) -> bool:
"""Returns true if target spec is ABI compatible to constraint spec"""
return self.architecture_compatible(target, constraint) and self.compiler_compatible(
target, constraint, loose=loose
)

View File

@@ -39,19 +39,21 @@ def _search_duplicate_compilers(error_cls):
import collections
import collections.abc
import glob
import inspect
import io
import itertools
import os
import pathlib
import pickle
import re
import warnings
from typing import Iterable, List, Set, Tuple
from urllib.request import urlopen
import llnl.util.lang
import spack.config
import spack.patch
import spack.paths
import spack.repo
import spack.spec
import spack.util.crypto
@@ -73,7 +75,9 @@ def __init__(self, summary, details):
self.details = tuple(details)
def __str__(self):
return self.summary + "\n" + "\n".join([" " + detail for detail in self.details])
if self.details:
return f"{self.summary}\n" + "\n".join(f" {detail}" for detail in self.details)
return self.summary
def __eq__(self, other):
if self.summary != other.summary or self.details != other.details:
@@ -210,6 +214,11 @@ def _search_duplicate_compilers(error_cls):
group="configs", tag="CFG-PACKAGES", description="Sanity checks on packages.yaml", kwargs=()
)
#: Sanity checks on packages.yaml
config_repos = AuditClass(
group="configs", tag="CFG-REPOS", description="Sanity checks on repositories", kwargs=()
)
@config_packages
def _search_duplicate_specs_in_externals(error_cls):
@@ -252,40 +261,6 @@ 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.23)"""
# TODO (v0.23): 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."""
@@ -367,6 +342,27 @@ def _ensure_all_virtual_packages_have_default_providers(error_cls):
]
@config_repos
def _ensure_no_folders_without_package_py(error_cls):
"""Check that we don't leave any folder without a package.py in repos"""
errors = []
for repository in spack.repo.PATH.repos:
missing = []
for entry in os.scandir(repository.packages_path):
if not entry.is_dir():
continue
package_py = pathlib.Path(entry.path) / spack.repo.package_file_name
if not package_py.exists():
missing.append(entry.path)
if missing:
summary = (
f"The '{repository.namespace}' repository misses a package.py file"
f" in the following folders"
)
errors.append(error_cls(summary=summary, details=[f"{x}" for x in missing]))
return errors
def _make_config_error(config_data, summary, error_cls):
s = io.StringIO()
s.write("Occurring in the following file:\n")
@@ -498,7 +494,7 @@ def _search_for_reserved_attributes_names_in_packages(pkgs, error_cls):
name_definitions = collections.defaultdict(list)
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
for cls_item in inspect.getmro(pkg_cls):
for cls_item in pkg_cls.__mro__:
for name in RESERVED_NAMES:
current_value = cls_item.__dict__.get(name)
if current_value is None:
@@ -527,7 +523,7 @@ def _ensure_all_package_names_are_lowercase(pkgs, error_cls):
badname_regex, errors = re.compile(r"[_A-Z]"), []
for pkg_name in pkgs:
if badname_regex.search(pkg_name):
error_msg = "Package name '{}' is either lowercase or conatine '_'".format(pkg_name)
error_msg = f"Package name '{pkg_name}' should be lowercase and must not contain '_'"
errors.append(error_cls(error_msg, []))
return errors
@@ -687,6 +683,88 @@ def _ensure_env_methods_are_ported_to_builders(pkgs, error_cls):
return errors
class DeprecatedMagicGlobals(ast.NodeVisitor):
def __init__(self, magic_globals: Iterable[str]):
super().__init__()
self.magic_globals: Set[str] = set(magic_globals)
# State to track whether we're in a class function
self.depth: int = 0
self.in_function: bool = False
self.path = (ast.Module, ast.ClassDef, ast.FunctionDef)
# Defined locals in the current function (heuristically at least)
self.locals: Set[str] = set()
# List of (name, lineno) tuples for references to magic globals
self.references_to_globals: List[Tuple[str, int]] = []
def descend_in_function_def(self, node: ast.AST) -> None:
if not isinstance(node, self.path[self.depth]):
return
self.depth += 1
if self.depth == len(self.path):
self.in_function = True
super().generic_visit(node)
if self.depth == len(self.path):
self.in_function = False
self.locals.clear()
self.depth -= 1
def generic_visit(self, node: ast.AST) -> None:
# Recurse into function definitions
if self.depth < len(self.path):
return self.descend_in_function_def(node)
elif not self.in_function:
return
elif isinstance(node, ast.Global):
for name in node.names:
if name in self.magic_globals:
self.references_to_globals.append((name, node.lineno))
elif isinstance(node, ast.Assign):
# visit the rhs before lhs
super().visit(node.value)
for target in node.targets:
super().visit(target)
elif isinstance(node, ast.Name) and node.id in self.magic_globals:
if isinstance(node.ctx, ast.Load) and node.id not in self.locals:
self.references_to_globals.append((node.id, node.lineno))
elif isinstance(node.ctx, ast.Store):
self.locals.add(node.id)
else:
super().generic_visit(node)
@package_properties
def _uses_deprecated_globals(pkgs, error_cls):
"""Ensure that packages do not use deprecated globals"""
errors = []
for pkg_name in pkgs:
# some packages scheduled to be removed in v0.23 are not worth fixing.
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
if all(v.get("deprecated", False) for v in pkg_cls.versions.values()):
continue
file = spack.repo.PATH.filename_for_package_name(pkg_name)
tree = ast.parse(open(file).read())
visitor = DeprecatedMagicGlobals(("std_cmake_args",))
visitor.visit(tree)
if visitor.references_to_globals:
errors.append(
error_cls(
f"Package '{pkg_name}' uses deprecated globals",
[
f"{file}:{line} references '{name}'"
for name, line in visitor.references_to_globals
],
)
)
return errors
@package_https_directives
def _linting_package_file(pkgs, error_cls):
"""Check for correctness of links"""

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@
all_core_root_specs,
ensure_clingo_importable_or_raise,
ensure_core_dependencies,
ensure_file_in_path_or_raise,
ensure_gpg_in_path_or_raise,
ensure_patchelf_in_path_or_raise,
)
@@ -19,6 +20,7 @@
"is_bootstrapping",
"ensure_bootstrap_configuration",
"ensure_core_dependencies",
"ensure_file_in_path_or_raise",
"ensure_gpg_in_path_or_raise",
"ensure_clingo_importable_or_raise",
"ensure_patchelf_in_path_or_raise",

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Common basic functions used through the spack.bootstrap package"""
import fnmatch
import importlib
import os.path
import re
import sys
@@ -28,7 +29,7 @@
def _python_import(module: str) -> bool:
try:
__import__(module)
importlib.import_module(module)
except ImportError:
return False
return True

View File

@@ -0,0 +1,154 @@
# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Bootstrap concrete specs for clingo
Spack uses clingo to concretize specs. When clingo itself needs to be bootstrapped from sources,
we need to rely on another mechanism to get a concrete spec that fits the current host.
This module contains the logic to get a concrete spec for clingo, starting from a prototype
JSON file for a similar platform.
"""
import pathlib
import sys
from typing import Dict, Optional, Tuple
import archspec.cpu
import spack.compiler
import spack.compilers
import spack.platforms
import spack.spec
import spack.traverse
from .config import spec_for_current_python
class ClingoBootstrapConcretizer:
def __init__(self, configuration):
self.host_platform = spack.platforms.host()
self.host_os = self.host_platform.operating_system("frontend")
self.host_target = archspec.cpu.host().family
self.host_architecture = spack.spec.ArchSpec.frontend_arch()
self.host_architecture.target = str(self.host_target)
self.host_compiler = self._valid_compiler_or_raise()
self.host_python = self.python_external_spec()
if str(self.host_platform) == "linux":
self.host_libc = self.libc_external_spec()
self.external_cmake, self.external_bison = self._externals_from_yaml(configuration)
def _valid_compiler_or_raise(self) -> "spack.compiler.Compiler":
if str(self.host_platform) == "linux":
compiler_name = "gcc"
elif str(self.host_platform) == "darwin":
compiler_name = "apple-clang"
elif str(self.host_platform) == "windows":
compiler_name = "msvc"
elif str(self.host_platform) == "freebsd":
compiler_name = "clang"
else:
raise RuntimeError(f"Cannot bootstrap clingo from sources on {self.host_platform}")
candidates = spack.compilers.compilers_for_spec(
compiler_name, arch_spec=self.host_architecture
)
if not candidates:
raise RuntimeError(
f"Cannot find any version of {compiler_name} to bootstrap clingo from sources"
)
candidates.sort(key=lambda x: x.spec.version, reverse=True)
return candidates[0]
def _externals_from_yaml(
self, configuration: "spack.config.Configuration"
) -> Tuple[Optional["spack.spec.Spec"], Optional["spack.spec.Spec"]]:
packages_yaml = configuration.get("packages")
requirements = {"cmake": "@3.20:", "bison": "@2.5:"}
selected: Dict[str, Optional["spack.spec.Spec"]] = {"cmake": None, "bison": None}
for pkg_name in ["cmake", "bison"]:
if pkg_name not in packages_yaml:
continue
candidates = packages_yaml[pkg_name].get("externals", [])
for candidate in candidates:
s = spack.spec.Spec(candidate["spec"], external_path=candidate["prefix"])
if not s.satisfies(requirements[pkg_name]):
continue
if not s.intersects(f"%{self.host_compiler.spec}"):
continue
if not s.intersects(f"arch={self.host_architecture}"):
continue
selected[pkg_name] = self._external_spec(s)
break
return selected["cmake"], selected["bison"]
def prototype_path(self) -> pathlib.Path:
"""Path to a prototype concrete specfile for clingo"""
parent_dir = pathlib.Path(__file__).parent
result = parent_dir / "prototypes" / f"clingo-{self.host_platform}-{self.host_target}.json"
if str(self.host_platform) == "linux":
# Using aarch64 as a fallback, since it has gnuconfig (x86_64 doesn't have it)
if not result.exists():
result = parent_dir / "prototypes" / f"clingo-{self.host_platform}-aarch64.json"
elif str(self.host_platform) == "freebsd":
result = parent_dir / "prototypes" / f"clingo-{self.host_platform}-amd64.json"
elif not result.exists():
raise RuntimeError(f"Cannot bootstrap clingo from sources on {self.host_platform}")
return result
def concretize(self) -> "spack.spec.Spec":
# Read the prototype and mark it NOT concrete
s = spack.spec.Spec.from_specfile(str(self.prototype_path()))
s._mark_concrete(False)
# Tweak it to conform to the host architecture
for node in s.traverse():
node.architecture.os = str(self.host_os)
node.compiler = self.host_compiler.spec
node.architecture = self.host_architecture
if node.name == "gcc-runtime":
node.versions = self.host_compiler.spec.versions
for edge in spack.traverse.traverse_edges([s], cover="edges"):
if edge.spec.name == "python":
edge.spec = self.host_python
if edge.spec.name == "bison" and self.external_bison:
edge.spec = self.external_bison
if edge.spec.name == "cmake" and self.external_cmake:
edge.spec = self.external_cmake
if "libc" in edge.virtuals:
edge.spec = self.host_libc
s._finalize_concretization()
# Work around the fact that the installer calls Spec.dependents() and
# we modified edges inconsistently
return s.copy()
def python_external_spec(self) -> "spack.spec.Spec":
"""Python external spec corresponding to the current running interpreter"""
result = spack.spec.Spec(spec_for_current_python(), external_path=sys.exec_prefix)
return self._external_spec(result)
def libc_external_spec(self) -> "spack.spec.Spec":
result = self.host_compiler.default_libc
return self._external_spec(result)
def _external_spec(self, initial_spec) -> "spack.spec.Spec":
initial_spec.namespace = "builtin"
initial_spec.compiler = self.host_compiler.spec
initial_spec.architecture = self.host_architecture
for flag_type in spack.spec.FlagMap.valid_compiler_flags():
initial_spec.compiler_flags[flag_type] = []
return spack.spec.parse_with_version_concrete(initial_spec)

View File

@@ -143,11 +143,7 @@ def _bootstrap_config_scopes() -> Sequence["spack.config.ConfigScope"]:
def _add_compilers_if_missing() -> None:
arch = spack.spec.ArchSpec.frontend_arch()
if not spack.compilers.compilers_for_arch(arch):
new_compilers = spack.compilers.find_new_compilers(
mixed_toolchain=sys.platform == "darwin"
)
if new_compilers:
spack.compilers.add_compilers_to_config(new_compilers)
spack.compilers.find_compilers()
@contextlib.contextmanager
@@ -156,7 +152,7 @@ def _ensure_bootstrap_configuration() -> Generator:
bootstrap_store_path = store_path()
user_configuration = _read_and_sanitize_configuration()
with spack.environment.no_active_environment():
with spack.platforms.prevent_cray_detection(), spack.platforms.use_platform(
with spack.platforms.use_platform(
spack.platforms.real_host()
), spack.repo.use_repositories(spack.paths.packages_path):
# Default configuration scopes excluding command line

View File

@@ -54,6 +54,7 @@
import spack.version
from ._common import _executables_in_store, _python_import, _root_spec, _try_import_from_store
from .clingo import ClingoBootstrapConcretizer
from .config import spack_python_interpreter, spec_for_current_python
#: Name of the file containing metadata about the bootstrapping source
@@ -268,15 +269,13 @@ def try_import(self, module: str, abstract_spec_str: str) -> bool:
# Try to build and install from sources
with spack_python_interpreter():
# Add hint to use frontend operating system on Cray
concrete_spec = spack.spec.Spec(abstract_spec_str + " ^" + spec_for_current_python())
if module == "clingo":
# TODO: remove when the old concretizer is deprecated # pylint: disable=fixme
concrete_spec._old_concretize( # pylint: disable=protected-access
deprecation_warning=False
)
bootstrapper = ClingoBootstrapConcretizer(configuration=spack.config.CONFIG)
concrete_spec = bootstrapper.concretize()
else:
concrete_spec = spack.spec.Spec(
abstract_spec_str + " ^" + spec_for_current_python()
)
concrete_spec.concretize()
msg = "[BOOTSTRAP MODULE {0}] Try installing '{1}' from sources"
@@ -303,14 +302,7 @@ def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bo
# might reduce compilation time by a fair amount
_add_externals_if_missing()
concrete_spec = spack.spec.Spec(abstract_spec_str)
if concrete_spec.name == "patchelf":
concrete_spec._old_concretize( # pylint: disable=protected-access
deprecation_warning=False
)
else:
concrete_spec.concretize()
concrete_spec = spack.spec.Spec(abstract_spec_str).concretized()
msg = "[BOOTSTRAP] Try installing '{0}' from sources"
tty.debug(msg.format(abstract_spec_str))
with spack.config.override(self.mirror_scope):
@@ -480,7 +472,8 @@ def ensure_clingo_importable_or_raise() -> None:
def gnupg_root_spec() -> str:
"""Return the root spec used to bootstrap GnuPG"""
return _root_spec("gnupg@2.3:")
root_spec_name = "win-gpg" if IS_WINDOWS else "gnupg"
return _root_spec(f"{root_spec_name}@2.3:")
def ensure_gpg_in_path_or_raise() -> None:
@@ -490,6 +483,19 @@ def ensure_gpg_in_path_or_raise() -> None:
)
def file_root_spec() -> str:
"""Return the root spec used to bootstrap file"""
root_spec_name = "win-file" if IS_WINDOWS else "file"
return _root_spec(root_spec_name)
def ensure_file_in_path_or_raise() -> None:
"""Ensure file is in the PATH or raise"""
return ensure_executables_in_path_or_raise(
executables=["file"], abstract_spec=file_root_spec()
)
def patchelf_root_spec() -> str:
"""Return the root spec used to bootstrap patchelf"""
# 0.13.1 is the last version not to require C++17.
@@ -573,14 +579,15 @@ def ensure_core_dependencies() -> None:
"""Ensure the presence of all the core dependencies."""
if sys.platform.lower() == "linux":
ensure_patchelf_in_path_or_raise()
if not IS_WINDOWS:
ensure_gpg_in_path_or_raise()
elif sys.platform == "win32":
ensure_file_in_path_or_raise()
ensure_gpg_in_path_or_raise()
ensure_clingo_importable_or_raise()
def all_core_root_specs() -> List[str]:
"""Return a list of all the core root specs that may be used to bootstrap Spack"""
return [clingo_root_spec(), gnupg_root_spec(), patchelf_root_spec()]
return [clingo_root_spec(), gnupg_root_spec(), patchelf_root_spec(), file_root_spec()]
def bootstrapping_sources(scope: Optional[str] = None):

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -88,7 +88,7 @@ def _core_requirements() -> List[RequiredResponseType]:
def _buildcache_requirements() -> List[RequiredResponseType]:
_buildcache_exes = {
"file": _missing("file", "required to analyze files for buildcaches"),
"file": _missing("file", "required to analyze files for buildcaches", system_only=False),
("gpg2", "gpg"): _missing("gpg2", "required to sign/verify buildcaches", False),
}
if platform.system().lower() == "darwin":
@@ -124,7 +124,7 @@ def _development_requirements() -> List[RequiredResponseType]:
# Ensure we trigger environment modifications if we have an environment
if BootstrapEnvironment.spack_yaml().exists():
with BootstrapEnvironment() as env:
env.update_syspath_and_environ()
env.load()
return [
_required_executable(

View File

@@ -457,9 +457,12 @@ def set_wrapper_variables(pkg, env):
env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format("{name}-{hash:7}"))
env.set(SPACK_DEBUG_LOG_DIR, spack.main.spack_working_dir)
# Find ccache binary and hand it to build environment
if spack.config.get("config:ccache"):
# Enable ccache in the compiler wrapper
env.set(SPACK_CCACHE_BINARY, spack.util.executable.which_string("ccache", required=True))
else:
# Avoid cache pollution if a build system forces `ccache <compiler wrapper invocation>`.
env.set("CCACHE_DISABLE", "1")
# Gather information about various types of dependencies
link_deps = set(pkg.spec.traverse(root=False, deptype=("link")))
@@ -1550,21 +1553,21 @@ class ModuleChangePropagator:
_PROTECTED_NAMES = ("package", "current_module", "modules_in_mro", "_set_attributes")
def __init__(self, package):
def __init__(self, package: spack.package_base.PackageBase) -> None:
self._set_self_attributes("package", package)
self._set_self_attributes("current_module", package.module)
#: Modules for the classes in the MRO up to PackageBase
modules_in_mro = []
for cls in inspect.getmro(type(package)):
module = cls.module
for cls in package.__class__.__mro__:
module = getattr(cls, "module", None)
if module == self.current_module:
continue
if module == spack.package_base:
if module is None or module is spack.package_base:
break
if module is self.current_module:
continue
modules_in_mro.append(module)
self._set_self_attributes("modules_in_mro", modules_in_mro)
self._set_self_attributes("_set_attributes", {})

View File

@@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import os
import os.path
import stat
@@ -549,13 +548,12 @@ def autoreconf(self, pkg, spec, prefix):
tty.warn("* a custom AUTORECONF phase in the package *")
tty.warn("*********************************************************")
with fs.working_dir(self.configure_directory):
m = inspect.getmodule(self.pkg)
# This line is what is needed most of the time
# --install, --verbose, --force
autoreconf_args = ["-ivf"]
autoreconf_args += self.autoreconf_search_path_args
autoreconf_args += self.autoreconf_extra_args
m.autoreconf(*autoreconf_args)
self.pkg.module.autoreconf(*autoreconf_args)
@property
def autoreconf_search_path_args(self):
@@ -579,7 +577,9 @@ def set_configure_or_die(self):
raise RuntimeError(msg.format(self.configure_directory))
# Monkey-patch the configure script in the corresponding module
inspect.getmodule(self.pkg).configure = Executable(self.configure_abs_path)
globals_for_pkg = spack.build_environment.ModuleChangePropagator(self.pkg)
globals_for_pkg.configure = Executable(self.configure_abs_path)
globals_for_pkg.propagate_changes_to_mro()
def configure_args(self):
"""Return the list of all the arguments that must be passed to configure,
@@ -596,7 +596,7 @@ def configure(self, pkg, spec, prefix):
options += self.configure_args()
with fs.working_dir(self.build_directory, create=True):
inspect.getmodule(self.pkg).configure(*options)
pkg.module.configure(*options)
def build(self, pkg, spec, prefix):
"""Run "make" on the build targets specified by the builder."""
@@ -604,12 +604,12 @@ def build(self, pkg, spec, prefix):
params = ["V=1"]
params += self.build_targets
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).make(*params)
pkg.module.make(*params)
def install(self, pkg, spec, prefix):
"""Run "make" on the install targets specified by the builder."""
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).make(*self.install_targets)
pkg.module.make(*self.install_targets)
spack.builder.run_after("build")(execute_build_time_tests)

View File

@@ -3,8 +3,6 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import llnl.util.filesystem as fs
import spack.builder
@@ -72,9 +70,7 @@ def check_args(self):
def build(self, pkg, spec, prefix):
"""Runs ``cargo install`` in the source directory"""
with fs.working_dir(self.build_directory):
inspect.getmodule(pkg).cargo(
"install", "--root", "out", "--path", ".", *self.build_args
)
pkg.module.cargo("install", "--root", "out", "--path", ".", *self.build_args)
def install(self, pkg, spec, prefix):
"""Copy build files into package prefix."""
@@ -86,4 +82,4 @@ def install(self, pkg, spec, prefix):
def check(self):
"""Run "cargo test"."""
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).cargo("test", *self.check_args)
self.pkg.module.cargo("test", *self.check_args)

View File

@@ -3,7 +3,6 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import collections.abc
import inspect
import os
import pathlib
import platform
@@ -108,6 +107,11 @@ def _conditional_cmake_defaults(pkg: spack.package_base.PackageBase, args: List[
if _supports_compilation_databases(pkg):
args.append(CMakeBuilder.define("CMAKE_EXPORT_COMPILE_COMMANDS", True))
# Enable MACOSX_RPATH by default when cmake_minimum_required < 3
# https://cmake.org/cmake/help/latest/policy/CMP0042.html
if pkg.spec.satisfies("platform=darwin") and cmake.satisfies("@3:"):
args.append(CMakeBuilder.define("CMAKE_POLICY_DEFAULT_CMP0042", "NEW"))
def generator(*names: str, default: Optional[str] = None):
"""The build system generator to use.
@@ -539,24 +543,24 @@ def cmake(self, pkg, spec, prefix):
options += self.cmake_args()
options.append(os.path.abspath(self.root_cmakelists_dir))
with fs.working_dir(self.build_directory, create=True):
inspect.getmodule(self.pkg).cmake(*options)
pkg.module.cmake(*options)
def build(self, pkg, spec, prefix):
"""Make the build targets"""
with fs.working_dir(self.build_directory):
if self.generator == "Unix Makefiles":
inspect.getmodule(self.pkg).make(*self.build_targets)
pkg.module.make(*self.build_targets)
elif self.generator == "Ninja":
self.build_targets.append("-v")
inspect.getmodule(self.pkg).ninja(*self.build_targets)
pkg.module.ninja(*self.build_targets)
def install(self, pkg, spec, prefix):
"""Make the install targets"""
with fs.working_dir(self.build_directory):
if self.generator == "Unix Makefiles":
inspect.getmodule(self.pkg).make(*self.install_targets)
pkg.module.make(*self.install_targets)
elif self.generator == "Ninja":
inspect.getmodule(self.pkg).ninja(*self.install_targets)
pkg.module.ninja(*self.install_targets)
spack.builder.run_after("build")(execute_build_time_tests)

View File

@@ -0,0 +1,192 @@
# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import glob
import importlib
import inspect
import os
import shutil
import time
import llnl.util.filesystem as fs
import llnl.util.tty as tty
import spack.build_systems.cmake
import spack.builder
import spack.util.log_parse
from spack.builder import run_after
from spack.directives import depends_on, requires, variant
from spack.package import CMakePackage
class CTestBuilder(spack.build_systems.cmake.CMakeBuilder):
"""
This builder mirrors the behavior of a CMakeBuilder, but all commands are run through
CTest. This ensures that xml files are created through CTest. This provides a unified
buildstamp and improved xml over the spack generated ones.
An additional phase is added for running tests post installation. This allows for things
like regression tests that can be used to monitior differences in behavior/performance
without failing the install.
"""
phases = ("cmake", "build", "install", "analysis")
@property
def std_cmake_args(self):
"""
Args to always supply to CMake. CDash args don't do anything if you don't submit
TODO: workout how to get the track,build,site mapped correctly
Currently this ignores the spack flags, and injests the CTestConfig.cmake files
In ExaWind it is hooked into additional infrastrucure.
The spack flags are not ingestible to the package as far as I can currently tell.
"""
args = super().std_cmake_args
if self.spec.variants["cdash_submit"].value:
args.extend(
[
"-D",
f"BUILDNAME={self.pkg.spec.name}",
"-D",
f"CTEST_BUILD_OPTIONS={self.pkg.spec.short_spec}",
"-D",
"SITE=TODO",
]
)
return args
def ctest_args(self):
args = ["-T", "Test"]
args.append("--stop-time")
overall_test_timeout = 60 * 60 * 4 # 4 hours TODO should probably be a variant
args.append(time.strftime("%H:%M:%S", time.localtime(time.time() + overall_test_timeout)))
args.append("-VV") # make sure lots of output can go to the log
# a way to parse additional information to ctest exectution.
# for ecample in exawind, we default to running unit-tests, but for nightly tests
# we expand to our regression test suite through this variant
extra_args = self.pkg.spec.variants["ctest_args"].value
if extra_args:
args.extend(extra_args.split())
return args
@property
def build_args(self):
"""
CTest arguments that translate to running a to the end of the build phase through CTest
"""
args = [
"--group",
self.pkg.spec.name,
"-T",
"Start",
"-T",
"Configure",
"-T",
"Build",
"-VV",
]
return args
@property
def submit_args(self):
"""
CTest arguments just for sumbmission. Allows us to split phases, where default CTest behavior is to configure, build, test and submit from a single command.
"""
args = ["-T", "Submit", "-V"]
return args
def submit_cdash(self, pkg, spec, prefix):
ctest = Executable(self.spec["cmake"].prefix.bin.ctest)
ctest.add_default_env("CTEST_PARALLEL_LEVEL", str(make_jobs))
build_env = os.environ.copy()
ctest(*self.submit_args, env=build_env)
def build(self, pkg, spec, prefix):
"""
The only reason to run through the CTest interface is if we want to submit to CDash with
unified CTest xml's.
If we aren't going to submit then we can just run as the CMakeBuilder
"""
if self.pkg.spec.variants["cdash_submit"].value:
ctest = Executable(self.spec["cmake"].prefix.bin.ctest)
ctest.add_default_env("CMAKE_BUILD_PARALLEL_LEVEL", str(make_jobs))
with fs.working_dir(self.build_directory):
build_env = os.environ.copy()
# have ctest run, but we still want to submit if there are build failures where spack would stop.
# check for errors and submit to cdash if there are failures
output = ctest(
*self.build_args, env=build_env, output=str.split, error=str.split
).split("\n")
errors, warnings = spack.util.log_parse.parse_log_events(output)
if len(errors) > 0:
errs = [str(e) for e in errors]
tty.warn(f"Errors: {errs}")
tty.warn(f"returncode {ctest.returncode}")
self.submit_cdash(pkg, spec, prefix)
raise BaseException(f"{self.pkg.spec.name} had build errors")
else:
super().build(pkg, spec, prefix)
def analysis(self, pkg, spec, prefix):
"""
This method currently runs tests post install to avoid the undesired side effect
of failing installs for failed tests with spack's built in testing infrastructure
"""
with working_dir(self.build_directory):
args = self.ctest_args()
tty.debug("{} running CTest".format(self.pkg.spec.name))
tty.debug("Running:: ctest" + " ".join(args))
ctest = Executable(self.spec["cmake"].prefix.bin.ctest)
ctest.add_default_env("CTEST_PARALLEL_LEVEL", str(make_jobs))
ctest.add_default_env("CMAKE_BUILD_PARALLEL_LEVEL", str(make_jobs))
build_env = os.environ.copy()
ctest(*args, "-j", str(make_jobs), env=build_env, fail_on_error=False)
if self.pkg.spec.variants["cdash_submit"].value:
self.submit_cdash(pkg, spec, prefix)
class CtestPackage(CMakePackage):
"""
This package's default behavior is to act as a Standard CMakePackage,
"""
CMakeBuilder = CTestBuilder
variant("cdash_submit", default=False, description="Submit results to cdash")
variant("ctest_args", default="", description="quoted string of arguments to send to ctest")
def setup_build_environment(self, env):
env.prepend_path("PYTHONPATH", os.environ["EXAWIND_MANAGER"])
def do_clean(self):
"""
A nice feature for development builds. Can be omitted from the final product.
"""
super().do_clean()
if not self.stage.managed_by_spack:
build_artifacts = glob.glob(os.path.join(self.stage.source_path, "spack-*"))
for f in build_artifacts:
if os.path.isfile(f):
os.remove(f)
if os.path.isdir(f):
shutil.rmtree(f)
ccjson = os.path.join(self.stage.source_path, "compile_commands.json")
if os.path.isfile(ccjson):
os.remove(ccjson)
@run_after("cmake")
def copy_compile_commands(self):
"""
A nice feature for development builds. Can be omitted from the final product.
"""
if self.spec.satisfies("dev_path=*"):
target = os.path.join(self.stage.source_path, "compile_commands.json")
source = os.path.join(self.build_directory, "compile_commands.json")
if os.path.isfile(source):
shutil.copyfile(source, target)

View File

@@ -3,6 +3,9 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import re
from typing import Iterable, List
import spack.variant
from spack.directives import conflicts, depends_on, variant
from spack.multimethod import when
@@ -44,6 +47,7 @@ class CudaPackage(PackageBase):
"87",
"89",
"90",
"90a",
)
# FIXME: keep cuda and cuda_arch separate to make usage easier until
@@ -70,6 +74,27 @@ def cuda_flags(arch_list):
for s in arch_list
]
@staticmethod
def compute_capabilities(arch_list: Iterable[str]) -> List[str]:
"""Adds a decimal place to each CUDA arch.
>>> compute_capabilities(['90', '90a'])
['9.0', '9.0a']
Args:
arch_list: A list of integer strings, optionally followed by a suffix.
Returns:
A list of float strings, optionally followed by a suffix
"""
pattern = re.compile(r"(\d+)")
capabilities = []
for arch in arch_list:
_, number, letter = re.split(pattern, arch)
number = "{0:.1f}".format(float(number) / 10.0)
capabilities.append(number + letter)
return capabilities
depends_on("cuda", when="+cuda")
# CUDA version vs Architecture
@@ -138,7 +163,7 @@ def cuda_flags(arch_list):
conflicts("%gcc@11.2:", when="+cuda ^cuda@:11.5")
conflicts("%gcc@12:", when="+cuda ^cuda@:11.8")
conflicts("%gcc@13:", when="+cuda ^cuda@:12.3")
conflicts("%gcc@14:", when="+cuda ^cuda@:12.5")
conflicts("%gcc@14:", when="+cuda ^cuda@:12.6")
conflicts("%clang@12:", when="+cuda ^cuda@:11.4.0")
conflicts("%clang@13:", when="+cuda ^cuda@:11.5")
conflicts("%clang@14:", when="+cuda ^cuda@:11.7")
@@ -146,6 +171,7 @@ def cuda_flags(arch_list):
conflicts("%clang@16:", when="+cuda ^cuda@:12.1")
conflicts("%clang@17:", when="+cuda ^cuda@:12.3")
conflicts("%clang@18:", when="+cuda ^cuda@:12.5")
conflicts("%clang@19:", when="+cuda ^cuda@:12.6")
# https://gist.github.com/ax3l/9489132#gistcomment-3860114
conflicts("%gcc@10", when="+cuda ^cuda@:11.4.0")

View File

@@ -3,8 +3,6 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import llnl.util.filesystem as fs
import spack.builder
@@ -82,7 +80,7 @@ def check_args(self):
def build(self, pkg, spec, prefix):
"""Runs ``go build`` in the source directory"""
with fs.working_dir(self.build_directory):
inspect.getmodule(pkg).go("build", *self.build_args)
pkg.module.go("build", *self.build_args)
def install(self, pkg, spec, prefix):
"""Install built binaries into prefix bin."""
@@ -95,4 +93,4 @@ def install(self, pkg, spec, prefix):
def check(self):
"""Run ``go test .`` in the source directory"""
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).go("test", *self.check_args)
self.pkg.module.go("test", *self.check_args)

View File

@@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
from typing import List
import llnl.util.filesystem as fs
@@ -103,12 +102,12 @@ def edit(self, pkg, spec, prefix):
def build(self, pkg, spec, prefix):
"""Run "make" on the build targets specified by the builder."""
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).make(*self.build_targets)
pkg.module.make(*self.build_targets)
def install(self, pkg, spec, prefix):
"""Run "make" on the install targets specified by the builder."""
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).make(*self.install_targets)
pkg.module.make(*self.install_targets)
spack.builder.run_after("build")(execute_build_time_tests)

View File

@@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import os
from typing import List
@@ -195,19 +194,19 @@ def meson(self, pkg, spec, prefix):
options += self.std_meson_args
options += self.meson_args()
with fs.working_dir(self.build_directory, create=True):
inspect.getmodule(self.pkg).meson(*options)
pkg.module.meson(*options)
def build(self, pkg, spec, prefix):
"""Make the build targets"""
options = ["-v"]
options += self.build_targets
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).ninja(*options)
pkg.module.ninja(*options)
def install(self, pkg, spec, prefix):
"""Make the install targets"""
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).ninja(*self.install_targets)
pkg.module.ninja(*self.install_targets)
spack.builder.run_after("build")(execute_build_time_tests)

View File

@@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
from typing import List # novm
import llnl.util.filesystem as fs
@@ -104,7 +103,7 @@ def msbuild_install_args(self):
def build(self, pkg, spec, prefix):
"""Run "msbuild" on the build targets specified by the builder."""
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).msbuild(
pkg.module.msbuild(
*self.std_msbuild_args,
*self.msbuild_args(),
self.define_targets(*self.build_targets),
@@ -114,6 +113,6 @@ def install(self, pkg, spec, prefix):
"""Run "msbuild" on the install targets specified by the builder.
This is INSTALL by default"""
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).msbuild(
pkg.module.msbuild(
*self.msbuild_install_args(), self.define_targets(*self.install_targets)
)

View File

@@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
from typing import List # novm
import llnl.util.filesystem as fs
@@ -132,9 +131,7 @@ def build(self, pkg, spec, prefix):
if self.makefile_name:
opts.append("/F{}".format(self.makefile_name))
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).nmake(
*opts, *self.build_targets, ignore_quotes=self.ignore_quotes
)
pkg.module.nmake(*opts, *self.build_targets, ignore_quotes=self.ignore_quotes)
def install(self, pkg, spec, prefix):
"""Run "nmake" on the install targets specified by the builder.
@@ -146,6 +143,4 @@ def install(self, pkg, spec, prefix):
opts.append("/F{}".format(self.makefile_name))
opts.append(self.define("PREFIX", fs.windows_sfn(prefix)))
with fs.working_dir(self.build_directory):
inspect.getmodule(self.pkg).nmake(
*opts, *self.install_targets, ignore_quotes=self.ignore_quotes
)
pkg.module.nmake(*opts, *self.install_targets, ignore_quotes=self.ignore_quotes)

View File

@@ -2,8 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import spack.builder
import spack.package_base
from spack.directives import build_system, extends
@@ -47,7 +45,7 @@ class OctaveBuilder(BaseBuilder):
def install(self, pkg, spec, prefix):
"""Install the package from the archive file"""
inspect.getmodule(self.pkg).octave(
pkg.module.octave(
"--quiet",
"--norc",
"--built-in-docstrings-file=/dev/null",

View File

@@ -3,7 +3,6 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Common utilities for managing intel oneapi packages."""
import getpass
import os
import platform
import shutil
@@ -13,6 +12,7 @@
from llnl.util.filesystem import HeaderList, LibraryList, find_libraries, join_path, mkdirp
from llnl.util.link_tree import LinkTree
import spack.util.path
from spack.build_environment import dso_suffix
from spack.directives import conflicts, license, redistribute, variant
from spack.package_base import InstallError
@@ -99,7 +99,7 @@ def install_component(self, installer_path):
# with other install depends on the userid. For root, we
# delete the installercache before and after install. For
# non root we redefine the HOME environment variable.
if getpass.getuser() == "root":
if spack.util.path.get_user() == "root":
shutil.rmtree("/var/intel/installercache", ignore_errors=True)
bash = Executable("bash")
@@ -122,7 +122,7 @@ def install_component(self, installer_path):
self.prefix,
)
if getpass.getuser() == "root":
if spack.util.path.get_user() == "root":
shutil.rmtree("/var/intel/installercache", ignore_errors=True)
# Some installers have a bug and do not return an error code when failing

View File

@@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import os
from typing import Iterable
@@ -134,7 +133,7 @@ def build_method(self):
def build_executable(self):
"""Returns the executable method to build the perl package"""
if self.build_method == "Makefile.PL":
build_executable = inspect.getmodule(self.pkg).make
build_executable = self.pkg.module.make
elif self.build_method == "Build.PL":
build_executable = Executable(os.path.join(self.pkg.stage.source_path, "Build"))
return build_executable
@@ -158,7 +157,7 @@ def configure(self, pkg, spec, prefix):
options = ["Build.PL", "--install_base", prefix]
options += self.configure_args()
inspect.getmodule(self.pkg).perl(*options)
pkg.module.perl(*options)
# It is possible that the shebang in the Build script that is created from
# Build.PL may be too long causing the build to fail. Patching the shebang

View File

@@ -4,7 +4,6 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import functools
import inspect
import operator
import os
import re
@@ -17,7 +16,7 @@
import llnl.util.filesystem as fs
import llnl.util.lang as lang
import llnl.util.tty as tty
from llnl.util.filesystem import HeaderList, LibraryList
from llnl.util.filesystem import HeaderList, LibraryList, join_path
import spack.builder
import spack.config
@@ -120,6 +119,12 @@ def skip_modules(self) -> Iterable[str]:
"""
return []
@property
def bindir(self) -> str:
"""Path to Python package's bindir, bin on unix like OS's Scripts on Windows"""
windows = self.spec.satisfies("platform=windows")
return join_path(self.spec.prefix, "Scripts" if windows else "bin")
def view_file_conflicts(self, view, merge_map):
"""Report all file conflicts, excepting special cases for python.
Specifically, this does not report errors for duplicate
@@ -222,7 +227,7 @@ def test_imports(self) -> None:
# Make sure we are importing the installed modules,
# not the ones in the source directory
python = inspect.getmodule(self).python # type: ignore[union-attr]
python = self.module.python
for module in self.import_modules:
with test_part(
self,
@@ -309,9 +314,9 @@ def get_external_python_for_prefix(self):
)
python_externals_detected = [
d.spec
for d in python_externals_detection.get("python", [])
if d.prefix == self.spec.external_path
spec
for spec in python_externals_detection.get("python", [])
if spec.external_path == self.spec.external_path
]
if python_externals_detected:
return python_externals_detected[0]
@@ -354,18 +359,10 @@ def homepage(cls) -> Optional[str]: # type: ignore[override]
return None
@lang.classproperty
def urls(cls) -> Optional[List[str]]:
def url(cls) -> Optional[str]:
if cls.pypi:
urls = [f"https://files.pythonhosted.org/packages/source/{cls.pypi[0]}/{cls.pypi}"]
assert cls.pypi.count("/") == 1, "PyPI class attribute must include a single slash"
name, file = cls.pypi.split("/")
name_dash_count = name.count("-")
if name_dash_count > 0:
# replace all but last dash with underscores for pypi.org listing changes
pypi = "/".join([name, file.replace("-", "_", name_dash_count)])
urls.append(f"https://files.pythonhosted.org/packages/source/{pypi[0]}/{pypi}")
return urls
return [None]
return f"https://files.pythonhosted.org/packages/source/{cls.pypi[0]}/{cls.pypi}"
return None
@lang.classproperty
def list_url(cls) -> Optional[str]: # type: ignore[override]

View File

@@ -2,8 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
from llnl.util.filesystem import working_dir
import spack.builder
@@ -66,17 +64,17 @@ def qmake_args(self):
def qmake(self, pkg, spec, prefix):
"""Run ``qmake`` to configure the project and generate a Makefile."""
with working_dir(self.build_directory):
inspect.getmodule(self.pkg).qmake(*self.qmake_args())
pkg.module.qmake(*self.qmake_args())
def build(self, pkg, spec, prefix):
"""Make the build targets"""
with working_dir(self.build_directory):
inspect.getmodule(self.pkg).make()
pkg.module.make()
def install(self, pkg, spec, prefix):
"""Make the install targets"""
with working_dir(self.build_directory):
inspect.getmodule(self.pkg).make("install")
pkg.module.make("install")
def check(self):
"""Search the Makefile for a ``check:`` target and runs it if found."""

View File

@@ -2,10 +2,10 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
from typing import Optional, Tuple
import llnl.util.lang as lang
from llnl.util.filesystem import mkdirp
from spack.directives import extends
@@ -37,6 +37,7 @@ def configure_vars(self):
def install(self, pkg, spec, prefix):
"""Installs an R package."""
mkdirp(pkg.module.r_lib_dir)
config_args = self.configure_args()
config_vars = self.configure_vars()
@@ -44,14 +45,14 @@ def install(self, pkg, spec, prefix):
args = ["--vanilla", "CMD", "INSTALL"]
if config_args:
args.append("--configure-args={0}".format(" ".join(config_args)))
args.append(f"--configure-args={' '.join(config_args)}")
if config_vars:
args.append("--configure-vars={0}".format(" ".join(config_vars)))
args.append(f"--configure-vars={' '.join(config_vars)}")
args.extend(["--library={0}".format(self.pkg.module.r_lib_dir), self.stage.source_path])
args.extend([f"--library={pkg.module.r_lib_dir}", self.stage.source_path])
inspect.getmodule(self.pkg).R(*args)
pkg.module.R(*args)
class RPackage(Package):
@@ -80,27 +81,21 @@ class RPackage(Package):
@lang.classproperty
def homepage(cls):
if cls.cran:
return "https://cloud.r-project.org/package=" + cls.cran
return f"https://cloud.r-project.org/package={cls.cran}"
elif cls.bioc:
return "https://bioconductor.org/packages/" + cls.bioc
return f"https://bioconductor.org/packages/{cls.bioc}"
@lang.classproperty
def url(cls):
if cls.cran:
return (
"https://cloud.r-project.org/src/contrib/"
+ cls.cran
+ "_"
+ str(list(cls.versions)[0])
+ ".tar.gz"
)
return f"https://cloud.r-project.org/src/contrib/{cls.cran}_{str(list(cls.versions)[0])}.tar.gz"
@lang.classproperty
def list_url(cls):
if cls.cran:
return "https://cloud.r-project.org/src/contrib/Archive/" + cls.cran + "/"
return f"https://cloud.r-project.org/src/contrib/Archive/{cls.cran}/"
@property
def git(self):
if self.bioc:
return "https://git.bioconductor.org/packages/" + self.bioc
return f"https://git.bioconductor.org/packages/{self.bioc}"

View File

@@ -3,7 +3,6 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import glob
import inspect
import spack.builder
import spack.package_base
@@ -52,10 +51,10 @@ def build(self, pkg, spec, prefix):
gemspecs = glob.glob("*.gemspec")
rakefiles = glob.glob("Rakefile")
if gemspecs:
inspect.getmodule(self.pkg).gem("build", "--norc", gemspecs[0])
pkg.module.gem("build", "--norc", gemspecs[0])
elif rakefiles:
jobs = inspect.getmodule(self.pkg).make_jobs
inspect.getmodule(self.pkg).rake("package", "-j{0}".format(jobs))
jobs = pkg.module.make_jobs
pkg.module.rake("package", "-j{0}".format(jobs))
else:
# Some Ruby packages only ship `*.gem` files, so nothing to build
pass
@@ -70,6 +69,6 @@ def install(self, pkg, spec, prefix):
# if --install-dir is not used, GEM_PATH is deleted from the
# environement, and Gems required to build native extensions will
# not be found. Those extensions are built during `gem install`.
inspect.getmodule(self.pkg).gem(
pkg.module.gem(
"install", "--norc", "--ignore-dependencies", "--install-dir", prefix, gems[0]
)

View File

@@ -2,8 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import spack.builder
import spack.package_base
from spack.directives import build_system, depends_on
@@ -63,8 +61,7 @@ def build_args(self, spec, prefix):
def build(self, pkg, spec, prefix):
"""Build the package."""
args = self.build_args(spec, prefix)
inspect.getmodule(self.pkg).scons(*args)
pkg.module.scons(*self.build_args(spec, prefix))
def install_args(self, spec, prefix):
"""Arguments to pass to install."""
@@ -72,9 +69,7 @@ def install_args(self, spec, prefix):
def install(self, pkg, spec, prefix):
"""Install the package."""
args = self.install_args(spec, prefix)
inspect.getmodule(self.pkg).scons("install", *args)
pkg.module.scons("install", *self.install_args(spec, prefix))
def build_test(self):
"""Run unit tests after build.

View File

@@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import os
import re
@@ -86,14 +85,13 @@ def import_modules(self):
def python(self, *args, **kwargs):
"""The python ``Executable``."""
inspect.getmodule(self).python(*args, **kwargs)
self.pkg.module.python(*args, **kwargs)
def test_imports(self):
"""Attempts to import modules of the installed package."""
# Make sure we are importing the installed modules,
# not the ones in the source directory
python = inspect.getmodule(self).python
for module in self.import_modules:
with spack.install_test.test_part(
self,
@@ -101,7 +99,7 @@ def test_imports(self):
purpose="checking import of {0}".format(module),
work_dir="spack-test",
):
python("-c", "import {0}".format(module))
self.python("-c", "import {0}".format(module))
@spack.builder.builder("sip")
@@ -136,7 +134,7 @@ def configure(self, pkg, spec, prefix):
"""Configure the package."""
# https://www.riverbankcomputing.com/static/Docs/sip/command_line_tools.html
args = ["--verbose", "--target-dir", inspect.getmodule(self.pkg).python_platlib]
args = ["--verbose", "--target-dir", pkg.module.python_platlib]
args.extend(self.configure_args())
# https://github.com/Python-SIP/sip/commit/cb0be6cb6e9b756b8b0db3136efb014f6fb9b766
@@ -155,7 +153,7 @@ def build(self, pkg, spec, prefix):
args = self.build_args()
with working_dir(self.build_directory):
inspect.getmodule(self.pkg).make(*args)
pkg.module.make(*args)
def build_args(self):
"""Arguments to pass to build."""
@@ -166,7 +164,7 @@ def install(self, pkg, spec, prefix):
args = self.install_args()
with working_dir(self.build_directory):
inspect.getmodule(self.pkg).make("install", *args)
pkg.module.make("install", *args)
def install_args(self):
"""Arguments to pass to install."""

View File

@@ -2,8 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
from llnl.util.filesystem import working_dir
import spack.builder
@@ -90,11 +88,11 @@ def build_directory(self):
def python(self, *args, **kwargs):
"""The python ``Executable``."""
inspect.getmodule(self.pkg).python(*args, **kwargs)
self.pkg.module.python(*args, **kwargs)
def waf(self, *args, **kwargs):
"""Runs the waf ``Executable``."""
jobs = inspect.getmodule(self.pkg).make_jobs
jobs = self.pkg.module.make_jobs
with working_dir(self.build_directory):
self.python("waf", "-j{0}".format(jobs), *args, **kwargs)

View File

@@ -6,12 +6,12 @@
import collections.abc
import copy
import functools
import inspect
from typing import List, Optional, Tuple
from llnl.util import lang
import spack.build_environment
import spack.multimethod
#: Builder classes, as registered by the "builder" decorator
BUILDER_CLS = {}
@@ -96,11 +96,10 @@ class hierarchy (look at AspellDictPackage for an example of that)
Args:
pkg (spack.package_base.PackageBase): package object for which we need a builder
"""
package_module = inspect.getmodule(pkg)
package_buildsystem = buildsystem_name(pkg)
default_builder_cls = BUILDER_CLS[package_buildsystem]
builder_cls_name = default_builder_cls.__name__
builder_cls = getattr(package_module, builder_cls_name, None)
builder_cls = getattr(pkg.module, builder_cls_name, None)
if builder_cls:
return builder_cls(pkg)
@@ -295,7 +294,11 @@ def _decorator(fn):
return _decorator
class BuilderMeta(PhaseCallbacksMeta, type(collections.abc.Sequence)): # type: ignore
class BuilderMeta(
PhaseCallbacksMeta,
spack.multimethod.MultiMethodMeta,
type(collections.abc.Sequence), # type: ignore
):
pass

View File

@@ -9,11 +9,11 @@
import llnl.util.lang
from llnl.util.filesystem import mkdirp
from llnl.util.symlink import symlink
import spack.config
import spack.error
import spack.fetch_strategy
import spack.mirror
import spack.paths
import spack.util.file_cache
import spack.util.path
@@ -74,23 +74,6 @@ def store(self, fetcher, relative_dest):
mkdirp(os.path.dirname(dst))
fetcher.archive(dst)
def symlink(self, mirror_ref):
"""Symlink a human readible path in our mirror to the actual
storage location."""
cosmetic_path = os.path.join(self.root, mirror_ref.cosmetic_path)
storage_path = os.path.join(self.root, mirror_ref.storage_path)
relative_dst = os.path.relpath(storage_path, start=os.path.dirname(cosmetic_path))
if not os.path.exists(cosmetic_path):
if os.path.lexists(cosmetic_path):
# In this case the link itself exists but it is broken: remove
# it and recreate it (in order to fix any symlinks broken prior
# to https://github.com/spack/spack/pull/13908)
os.unlink(cosmetic_path)
mkdirp(os.path.dirname(cosmetic_path))
symlink(relative_dst, cosmetic_path)
#: Spack's local cache for downloaded source archives
FETCH_CACHE: Union[spack.fetch_strategy.FsCache, llnl.util.lang.Singleton] = (

View File

@@ -38,6 +38,7 @@
import spack.paths
import spack.repo
import spack.spec
import spack.stage
import spack.util.git
import spack.util.gpg as gpg_util
import spack.util.spack_yaml as syaml
@@ -1107,9 +1108,10 @@ def main_script_replacements(cmd):
if cdash_handler and cdash_handler.auth_token:
try:
cdash_handler.populate_buildgroup(all_job_names)
except (SpackError, HTTPError, URLError) as err:
except (SpackError, HTTPError, URLError, TimeoutError) as err:
tty.warn(f"Problem populating buildgroup: {err}")
else:
elif cdash_config:
# warn only if there was actually a CDash configuration.
tty.warn("Unable to populate buildgroup without CDash credentials")
service_job_retries = {
@@ -1370,15 +1372,6 @@ def can_verify_binaries():
return len(gpg_util.public_keys()) >= 1
def _push_to_build_cache(spec: spack.spec.Spec, sign_binaries: bool, mirror_url: str) -> None:
"""Unchecked version of the public API, for easier mocking"""
bindist.push_or_raise(
spec,
spack.mirror.Mirror.from_url(mirror_url).push_url,
bindist.PushOptions(force=True, unsigned=not sign_binaries),
)
def push_to_build_cache(spec: spack.spec.Spec, mirror_url: str, sign_binaries: bool) -> bool:
"""Push one or more binary packages to the mirror.
@@ -1389,20 +1382,15 @@ def push_to_build_cache(spec: spack.spec.Spec, mirror_url: str, sign_binaries: b
sign_binaries: If True, spack will attempt to sign binary package before pushing.
"""
tty.debug(f"Pushing to build cache ({'signed' if sign_binaries else 'unsigned'})")
signing_key = bindist.select_signing_key() if sign_binaries else None
mirror = spack.mirror.Mirror.from_url(mirror_url)
try:
_push_to_build_cache(spec, sign_binaries, mirror_url)
with bindist.make_uploader(mirror, signing_key=signing_key) as uploader:
uploader.push_or_raise([spec])
return True
except bindist.PushToBuildCacheError as e:
tty.error(str(e))
tty.error(f"Problem writing to {mirror_url}: {e}")
return False
except Exception as e:
# TODO (zackgalbreath): write an adapter for boto3 exceptions so we can catch a specific
# exception instead of parsing str(e)...
msg = str(e)
if any(x in msg for x in ["Access Denied", "InvalidAccessKeyId"]):
tty.error(f"Permission problem writing to {mirror_url}: {msg}")
return False
raise
def remove_other_mirrors(mirrors_to_keep, scope=None):
@@ -1448,10 +1436,6 @@ def copy_stage_logs_to_artifacts(job_spec: spack.spec.Spec, job_log_dir: str) ->
job_log_dir: path into which build log should be copied
"""
tty.debug(f"job spec: {job_spec}")
if not job_spec:
msg = f"Cannot copy stage logs: job spec ({job_spec}) is required"
tty.error(msg)
return
try:
pkg_cls = spack.repo.PATH.get_pkg_class(job_spec.name)
@@ -2083,7 +2067,7 @@ def read_broken_spec(broken_spec_url):
"""
try:
_, _, fs = web_util.read_from_url(broken_spec_url)
except (URLError, web_util.SpackWebError, HTTPError):
except web_util.SpackWebError:
tty.warn(f"Unable to read broken spec from {broken_spec_url}")
return None

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import argparse
import importlib
import os
import re
import sys
@@ -114,8 +115,8 @@ def get_module(cmd_name):
try:
# Try to import the command from the built-in directory
module_name = "%s.%s" % (__name__, pname)
module = __import__(module_name, fromlist=[pname, SETUP_PARSER, DESCRIPTION], level=0)
module_name = f"{__name__}.{pname}"
module = importlib.import_module(module_name)
tty.debug("Imported {0} from built-in commands".format(pname))
except ImportError:
module = spack.extensions.get_module(cmd_name)

View File

@@ -115,15 +115,11 @@ def audit(parser, args):
def _process_reports(reports):
for check, errors in reports:
if errors:
msg = "{0}: {1} issue{2} found".format(
check, len(errors), "" if len(errors) == 1 else "s"
)
header = "@*b{" + msg + "}"
print(cl.colorize(header))
status = f"{len(errors)} issue{'' if len(errors) == 1 else 's'} found"
print(cl.colorize(f"{check}: @*r{{{status}}}"))
numdigits = len(str(len(errors)))
for idx, error in enumerate(errors):
print(str(idx + 1) + ". " + str(error))
print(f"{idx + 1:>{numdigits}}. {error}")
raise SystemExit(1)
else:
msg = "{0}: 0 issues found.".format(check)
header = "@*b{" + msg + "}"
print(cl.colorize(header))
print(cl.colorize(f"{check}: @*g{{passed}}"))

View File

@@ -3,28 +3,24 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import argparse
import copy
import glob
import hashlib
import json
import multiprocessing
import multiprocessing.pool
import os
import shutil
import sys
import tempfile
from typing import Dict, List, Optional, Tuple, Union
from typing import List, Tuple
import llnl.util.tty as tty
from llnl.string import plural
from llnl.util.lang import elide_list
from llnl.util.lang import elide_list, stable_partition
import spack.binary_distribution as bindist
import spack.cmd
import spack.config
import spack.deptypes as dt
import spack.environment as ev
import spack.error
import spack.hash_types as ht
import spack.mirror
import spack.oci.oci
import spack.oci.opener
@@ -35,28 +31,12 @@
import spack.store
import spack.user_environment
import spack.util.crypto
import spack.util.parallel
import spack.util.url as url_util
import spack.util.web as web_util
from spack import traverse
from spack.build_environment import determine_number_of_jobs
from spack.cmd import display_specs
from spack.cmd.common import arguments
from spack.oci.image import (
Digest,
ImageReference,
default_config,
default_index_tag,
default_manifest,
default_tag,
tag_is_spec,
)
from spack.oci.oci import (
copy_missing_layers_with_retry,
get_manifest_and_config_with_retry,
list_tags,
upload_blob_with_retry,
upload_manifest_with_retry,
)
from spack.spec import Spec, save_dependency_specfiles
description = "create, download and install binary packages"
@@ -112,6 +92,17 @@ def setup_parser(subparser: argparse.ArgumentParser):
"Alternatively, one can decide to build a cache for only the package or only the "
"dependencies",
)
with_or_without_build_deps = push.add_mutually_exclusive_group()
with_or_without_build_deps.add_argument(
"--with-build-dependencies",
action="store_true",
help="include build dependencies in the buildcache",
)
with_or_without_build_deps.add_argument(
"--without-build-dependencies",
action="store_true",
help="exclude build dependencies from the buildcache",
)
push.add_argument(
"--fail-fast",
action="store_true",
@@ -329,39 +320,6 @@ def _format_spec(spec: Spec) -> str:
return spec.cformat("{name}{@version}{/hash:7}")
def _progress(i: int, total: int):
if total > 1:
digits = len(str(total))
return f"[{i+1:{digits}}/{total}] "
return ""
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 _skip_no_redistribute_for_public(specs):
remaining_specs = list()
removed_specs = list()
@@ -381,6 +339,45 @@ def _skip_no_redistribute_for_public(specs):
return remaining_specs
class PackagesAreNotInstalledError(spack.error.SpackError):
"""Raised when a list of specs is not installed but picked to be packaged."""
def __init__(self, specs: List[Spec]):
super().__init__(
"Cannot push non-installed packages",
", ".join(elide_list([_format_spec(s) for s in specs], 5)),
)
class PackageNotInstalledError(spack.error.SpackError):
"""Raised when a spec is not installed but picked to be packaged."""
def _specs_to_be_packaged(
requested: List[Spec], things_to_install: str, build_deps: bool
) -> List[Spec]:
"""Collect all non-external with or without roots and dependencies"""
if "dependencies" not in things_to_install:
deptype = dt.NONE
elif build_deps:
deptype = dt.ALL
else:
deptype = dt.RUN | dt.LINK | dt.TEST
specs = [
s
for s in traverse.traverse_nodes(
requested,
root="package" in things_to_install,
deptype=deptype,
order="breadth",
key=traverse.by_dag_hash,
)
if not s.external
]
specs.reverse()
return specs
def push_fn(args):
"""create a binary package and push it to a mirror"""
if args.spec_file:
@@ -394,13 +391,8 @@ def push_fn(args):
else:
roots = spack.cmd.require_active_env(cmd_name="buildcache push").concrete_roots()
mirror: spack.mirror.Mirror = args.mirror
# Check if this is an OCI image.
try:
target_image = spack.oci.oci.image_from_mirror(mirror)
except ValueError:
target_image = None
mirror = args.mirror
assert isinstance(mirror, spack.mirror.Mirror)
push_url = mirror.push_url
@@ -411,92 +403,52 @@ def push_fn(args):
unsigned = not (args.key or args.signed)
# For OCI images, we require dependencies to be pushed for now.
if target_image:
if "dependencies" not in args.things_to_install:
tty.die("Dependencies must be pushed for OCI images.")
if not unsigned:
tty.warn(
"Code signing is currently not supported for OCI images. "
"Use --unsigned to silence this warning."
)
if mirror.push_url.startswith("oci://") and not unsigned:
tty.warn(
"Code signing is currently not supported for OCI images. "
"Use --unsigned to silence this warning."
)
unsigned = True
# This is a list of installed, non-external specs.
specs = bindist.specs_to_be_packaged(
# Select a signing key, or None if unsigned.
signing_key = None if unsigned else (args.key or bindist.select_signing_key())
specs = _specs_to_be_packaged(
roots,
root="package" in args.things_to_install,
dependencies="dependencies" in args.things_to_install,
things_to_install=args.things_to_install,
build_deps=args.with_build_dependencies or not args.without_build_dependencies,
)
if not args.private:
specs = _skip_no_redistribute_for_public(specs)
# When pushing multiple specs, print the url once ahead of time, as well as how
# many specs are being pushed.
if len(specs) > 1:
tty.info(f"Selected {len(specs)} specs to push to {push_url}")
failed = []
# TODO: unify this logic in the future.
if target_image:
base_image = ImageReference.from_string(args.base_image) if args.base_image else None
with tempfile.TemporaryDirectory(
dir=spack.stage.get_stage_root()
) as tmpdir, _make_pool() as pool:
skipped, base_images, checksums = _push_oci(
target_image=target_image,
base_image=base_image,
installed_specs_with_deps=specs,
force=args.force,
tmpdir=tmpdir,
pool=pool,
)
# Apart from creating manifests for each individual spec, we allow users to create a
# separate image tag for all root specs and their runtime dependencies.
if args.tag:
tagged_image = target_image.with_tag(args.tag)
# _push_oci may not populate base_images if binaries were already in the registry
for spec in roots:
_update_base_images(
base_image=base_image,
target_image=target_image,
spec=spec,
base_image_cache=base_images,
)
_put_manifest(base_images, checksums, tagged_image, tmpdir, None, None, *roots)
tty.info(f"Tagged {tagged_image}")
else:
skipped = []
for i, spec in enumerate(specs):
try:
bindist.push_or_raise(
spec,
push_url,
bindist.PushOptions(
force=args.force,
unsigned=unsigned,
key=args.key,
regenerate_index=args.update_index,
),
# Pushing not installed specs is an error. Either fail fast or populate the error list and
# push installed package in best effort mode.
failed: List[Tuple[Spec, BaseException]] = []
with spack.store.STORE.db.read_transaction():
if any(not s.installed for s in specs):
specs, not_installed = stable_partition(specs, lambda s: s.installed)
if args.fail_fast:
raise PackagesAreNotInstalledError(not_installed)
else:
failed.extend(
(s, PackageNotInstalledError("package not installed")) for s in not_installed
)
msg = f"{_progress(i, len(specs))}Pushed {_format_spec(spec)}"
if len(specs) == 1:
msg += f" to {push_url}"
tty.info(msg)
except bindist.NoOverwriteException:
skipped.append(_format_spec(spec))
# Catch any other exception unless the fail fast option is set
except Exception as e:
if args.fail_fast or isinstance(
e, (bindist.PickKeyException, bindist.NoKeyException)
):
raise
failed.append((_format_spec(spec), e))
with bindist.make_uploader(
mirror=mirror,
force=args.force,
update_index=args.update_index,
signing_key=signing_key,
base_image=args.base_image,
) as uploader:
skipped, upload_errors = uploader.push(specs=specs)
failed.extend(upload_errors)
if not upload_errors and args.tag:
uploader.tag(args.tag, roots)
if skipped:
if len(specs) == 1:
@@ -508,7 +460,7 @@ def push_fn(args):
"The following {} specs were skipped as they already exist in the buildcache:\n"
" {}\n"
" Use --force to overwrite them.".format(
len(skipped), ", ".join(elide_list(skipped, 5))
len(skipped), ", ".join(elide_list([_format_spec(s) for s in skipped], 5))
)
)
@@ -519,389 +471,15 @@ def push_fn(args):
raise spack.error.SpackError(
f"The following {len(failed)} errors occurred while pushing specs to the buildcache",
"\n".join(
elide_list([f" {spec}: {e.__class__.__name__}: {e}" for spec, e in failed], 5)
),
)
# Update the index if requested
# TODO: remove update index logic out of bindist; should be once after all specs are pushed
# not once per spec.
if target_image and len(skipped) < len(specs) and args.update_index:
with tempfile.TemporaryDirectory(
dir=spack.stage.get_stage_root()
) as tmpdir, _make_pool() as pool:
_update_index_oci(target_image, tmpdir, pool)
def _get_spack_binary_blob(image_ref: ImageReference) -> Optional[spack.oci.oci.Blob]:
"""Get the spack tarball layer digests and size if it exists"""
try:
manifest, config = get_manifest_and_config_with_retry(image_ref)
return spack.oci.oci.Blob(
compressed_digest=Digest.from_string(manifest["layers"][-1]["digest"]),
uncompressed_digest=Digest.from_string(config["rootfs"]["diff_ids"][-1]),
size=manifest["layers"][-1]["size"],
)
except Exception:
return None
def _push_single_spack_binary_blob(image_ref: ImageReference, spec: spack.spec.Spec, tmpdir: str):
filename = os.path.join(tmpdir, f"{spec.dag_hash()}.tar.gz")
# Create an oci.image.layer aka tarball of the package
compressed_tarfile_checksum, tarfile_checksum = spack.oci.oci.create_tarball(spec, filename)
blob = spack.oci.oci.Blob(
Digest.from_sha256(compressed_tarfile_checksum),
Digest.from_sha256(tarfile_checksum),
os.path.getsize(filename),
)
# Upload the blob
upload_blob_with_retry(image_ref, file=filename, digest=blob.compressed_digest)
# delete the file
os.unlink(filename)
return blob
def _retrieve_env_dict_from_config(config: dict) -> dict:
"""Retrieve the environment variables from the image config file.
Sets a default value for PATH if it is not present.
Args:
config (dict): The image config file.
Returns:
dict: The environment variables.
"""
env = {"PATH": "/bin:/usr/bin"}
if "Env" in config.get("config", {}):
for entry in config["config"]["Env"]:
key, value = entry.split("=", 1)
env[key] = value
return env
def _archspec_to_gooarch(spec: spack.spec.Spec) -> str:
name = spec.target.family.name
name_map = {"aarch64": "arm64", "x86_64": "amd64"}
return name_map.get(name, name)
def _put_manifest(
base_images: Dict[str, Tuple[dict, dict]],
checksums: Dict[str, spack.oci.oci.Blob],
image_ref: ImageReference,
tmpdir: str,
extra_config: Optional[dict],
annotations: Optional[dict],
*specs: spack.spec.Spec,
):
architecture = _archspec_to_gooarch(specs[0])
dependencies = list(
reversed(
list(
s
for s in traverse.traverse_nodes(
specs, order="topo", deptype=("link", "run"), root=True
elide_list(
[
f" {_format_spec(spec)}: {e.__class__.__name__}: {e}"
for spec, e in failed
],
5,
)
if not s.external
)
)
)
base_manifest, base_config = base_images[architecture]
env = _retrieve_env_dict_from_config(base_config)
# If the base image uses `vnd.docker.distribution.manifest.v2+json`, then we use that too.
# This is because Singularity / Apptainer is very strict about not mixing them.
base_manifest_mediaType = base_manifest.get(
"mediaType", "application/vnd.oci.image.manifest.v1+json"
)
use_docker_format = (
base_manifest_mediaType == "application/vnd.docker.distribution.manifest.v2+json"
)
spack.user_environment.environment_modifications_for_specs(*specs).apply_modifications(env)
# Create an oci.image.config file
config = copy.deepcopy(base_config)
# Add the diff ids of the dependencies
for s in dependencies:
config["rootfs"]["diff_ids"].append(str(checksums[s.dag_hash()].uncompressed_digest))
# Set the environment variables
config["config"]["Env"] = [f"{k}={v}" for k, v in env.items()]
if extra_config:
# From the OCI v1.0 spec:
# > Any extra fields in the Image JSON struct are considered implementation
# > specific and MUST be ignored by any implementations which are unable to
# > interpret them.
config.update(extra_config)
config_file = os.path.join(tmpdir, f"{specs[0].dag_hash()}.config.json")
with open(config_file, "w") as f:
json.dump(config, f, separators=(",", ":"))
config_file_checksum = Digest.from_sha256(
spack.util.crypto.checksum(hashlib.sha256, config_file)
)
# Upload the config file
upload_blob_with_retry(image_ref, file=config_file, digest=config_file_checksum)
manifest = {
"mediaType": base_manifest_mediaType,
"schemaVersion": 2,
"config": {
"mediaType": base_manifest["config"]["mediaType"],
"digest": str(config_file_checksum),
"size": os.path.getsize(config_file),
},
"layers": [
*(layer for layer in base_manifest["layers"]),
*(
{
"mediaType": (
"application/vnd.docker.image.rootfs.diff.tar.gzip"
if use_docker_format
else "application/vnd.oci.image.layer.v1.tar+gzip"
),
"digest": str(checksums[s.dag_hash()].compressed_digest),
"size": checksums[s.dag_hash()].size,
}
for s in dependencies
),
],
}
if not use_docker_format and annotations:
manifest["annotations"] = annotations
# Finally upload the manifest
upload_manifest_with_retry(image_ref, manifest=manifest)
# delete the config file
os.unlink(config_file)
def _update_base_images(
*,
base_image: Optional[ImageReference],
target_image: ImageReference,
spec: spack.spec.Spec,
base_image_cache: Dict[str, Tuple[dict, dict]],
):
"""For a given spec and base image, copy the missing layers of the base image with matching
arch to the registry of the target image. If no base image is specified, create a dummy
manifest and config file."""
architecture = _archspec_to_gooarch(spec)
if architecture in base_image_cache:
return
if base_image is None:
base_image_cache[architecture] = (
default_manifest(),
default_config(architecture, "linux"),
)
else:
base_image_cache[architecture] = copy_missing_layers_with_retry(
base_image, target_image, architecture
)
def _push_oci(
*,
target_image: ImageReference,
base_image: Optional[ImageReference],
installed_specs_with_deps: List[Spec],
tmpdir: str,
pool: MaybePool,
force: bool = False,
) -> Tuple[List[str], Dict[str, Tuple[dict, dict]], Dict[str, spack.oci.oci.Blob]]:
"""Push specs to an OCI registry
Args:
image_ref: The target OCI image
base_image: Optional base image, which will be copied to the target registry.
installed_specs_with_deps: The installed specs to push, excluding externals,
including deps, ordered from roots to leaves.
force: Whether to overwrite existing layers and manifests in the buildcache.
Returns:
A tuple consisting of the list of skipped specs already in the build cache,
a dictionary mapping architectures to base image manifests and configs,
and a dictionary mapping each spec's dag hash to a blob.
"""
# Reverse the order
installed_specs_with_deps = list(reversed(installed_specs_with_deps))
# Spec dag hash -> blob
checksums: Dict[str, spack.oci.oci.Blob] = {}
# arch -> (manifest, config)
base_images: Dict[str, Tuple[dict, dict]] = {}
# Specs not uploaded because they already exist
skipped = []
if not force:
tty.info("Checking for existing specs in the buildcache")
to_be_uploaded = []
tags_to_check = (target_image.with_tag(default_tag(s)) for s in installed_specs_with_deps)
available_blobs = pool.map(_get_spack_binary_blob, tags_to_check)
for spec, maybe_blob in zip(installed_specs_with_deps, available_blobs):
if maybe_blob is not None:
checksums[spec.dag_hash()] = maybe_blob
skipped.append(_format_spec(spec))
else:
to_be_uploaded.append(spec)
else:
to_be_uploaded = installed_specs_with_deps
if not to_be_uploaded:
return skipped, base_images, checksums
tty.info(
f"{len(to_be_uploaded)} specs need to be pushed to "
f"{target_image.domain}/{target_image.name}"
)
# Upload blobs
new_blobs = pool.starmap(
_push_single_spack_binary_blob, ((target_image, spec, tmpdir) for spec in to_be_uploaded)
)
# And update the spec to blob mapping
for spec, blob in zip(to_be_uploaded, new_blobs):
checksums[spec.dag_hash()] = blob
# Copy base images if necessary
for spec in to_be_uploaded:
_update_base_images(
base_image=base_image,
target_image=target_image,
spec=spec,
base_image_cache=base_images,
)
def extra_config(spec: Spec):
spec_dict = spec.to_dict(hash=ht.dag_hash)
spec_dict["buildcache_layout_version"] = 1
spec_dict["binary_cache_checksum"] = {
"hash_algorithm": "sha256",
"hash": checksums[spec.dag_hash()].compressed_digest.digest,
}
return spec_dict
# Upload manifests
tty.info("Uploading manifests")
pool.starmap(
_put_manifest,
(
(
base_images,
checksums,
target_image.with_tag(default_tag(spec)),
tmpdir,
extra_config(spec),
{"org.opencontainers.image.description": spec.format()},
spec,
)
for spec in to_be_uploaded
),
)
# Print the image names of the top-level specs
for spec in to_be_uploaded:
tty.info(f"Pushed {_format_spec(spec)} to {target_image.with_tag(default_tag(spec))}")
return skipped, base_images, checksums
def _config_from_tag(image_ref: ImageReference, tag: str) -> Optional[dict]:
# Don't allow recursion here, since Spack itself always uploads
# vnd.oci.image.manifest.v1+json, not vnd.oci.image.index.v1+json
_, config = get_manifest_and_config_with_retry(image_ref.with_tag(tag), tag, recurse=0)
# Do very basic validation: if "spec" is a key in the config, it
# must be a Spec object too.
return config if "spec" in config else None
def _update_index_oci(image_ref: ImageReference, tmpdir: str, pool: MaybePool) -> None:
tags = list_tags(image_ref)
# Fetch all image config files in parallel
spec_dicts = pool.starmap(
_config_from_tag, ((image_ref, tag) for tag in tags if tag_is_spec(tag))
)
# Populate the database
db_root_dir = os.path.join(tmpdir, "db_root")
db = bindist.BuildCacheDatabase(db_root_dir)
for spec_dict in spec_dicts:
spec = Spec.from_dict(spec_dict)
db.add(spec, directory_layout=None)
db.mark(spec, "in_buildcache", True)
# Create the index.json file
index_json_path = os.path.join(tmpdir, "index.json")
with open(index_json_path, "w") as f:
db._write_to_file(f)
# Create an empty config.json file
empty_config_json_path = os.path.join(tmpdir, "config.json")
with open(empty_config_json_path, "wb") as f:
f.write(b"{}")
# Upload the index.json file
index_shasum = Digest.from_sha256(spack.util.crypto.checksum(hashlib.sha256, index_json_path))
upload_blob_with_retry(image_ref, file=index_json_path, digest=index_shasum)
# Upload the config.json file
empty_config_digest = Digest.from_sha256(
spack.util.crypto.checksum(hashlib.sha256, empty_config_json_path)
)
upload_blob_with_retry(image_ref, file=empty_config_json_path, digest=empty_config_digest)
# Push a manifest file that references the index.json file as a layer
# Notice that we push this as if it is an image, which it of course is not.
# When the ORAS spec becomes official, we can use that instead of a fake image.
# For now we just use the OCI image spec, so that we don't run into issues with
# automatic garbage collection of blobs that are not referenced by any image manifest.
oci_manifest = {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"schemaVersion": 2,
# Config is just an empty {} file for now, and irrelevant
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": str(empty_config_digest),
"size": os.path.getsize(empty_config_json_path),
},
# The buildcache index is the only layer, and is not a tarball, we lie here.
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": str(index_shasum),
"size": os.path.getsize(index_json_path),
}
],
}
upload_manifest_with_retry(image_ref.with_tag(default_index_tag), oci_manifest)
def install_fn(args):
@@ -1182,14 +760,15 @@ def update_index(mirror: spack.mirror.Mirror, update_keys=False):
if image_ref:
with tempfile.TemporaryDirectory(
dir=spack.stage.get_stage_root()
) as tmpdir, _make_pool() as pool:
_update_index_oci(image_ref, tmpdir, pool)
) as tmpdir, spack.util.parallel.make_concurrent_executor() as executor:
bindist._oci_update_index(image_ref, tmpdir, executor)
return
# Otherwise, assume a normal mirror.
url = mirror.push_url
bindist.generate_package_index(url_util.join(url, bindist.build_cache_relative_path()))
with tempfile.TemporaryDirectory(dir=spack.stage.get_stage_root()) as tmpdir:
bindist._url_generate_package_index(url, tmpdir)
if update_keys:
keys_url = url_util.join(
@@ -1197,7 +776,8 @@ def update_index(mirror: spack.mirror.Mirror, update_keys=False):
)
try:
bindist.generate_key_index(keys_url)
with tempfile.TemporaryDirectory(dir=spack.stage.get_stage_root()) as tmpdir:
bindist.generate_key_index(keys_url, tmpdir)
except bindist.CannotListKeys as e:
# Do not error out if listing keys went wrong. This usually means that the _gpg path
# does not exist. TODO: distinguish between this and other errors.

View File

@@ -7,6 +7,7 @@
import copy
import os
import re
import shlex
import sys
from argparse import ArgumentParser, Namespace
from typing import IO, Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
@@ -18,6 +19,7 @@
import spack.cmd
import spack.main
import spack.paths
import spack.platforms
from spack.main import section_descriptions
description = "list available spack commands"
@@ -139,7 +141,7 @@ def usage(self, usage: str) -> str:
cmd = self.parser.prog.replace(" ", "-")
if cmd in self.documented:
string += "\n:ref:`More documentation <cmd-{0}>`\n".format(cmd)
string = f"{string}\n:ref:`More documentation <cmd-{cmd}>`\n"
return string
@@ -249,33 +251,27 @@ def body(
Function body.
"""
if positionals:
return """
return f"""
if $list_options
then
{0}
{self.optionals(optionals)}
else
{1}
{self.positionals(positionals)}
fi
""".format(
self.optionals(optionals), self.positionals(positionals)
)
"""
elif subcommands:
return """
return f"""
if $list_options
then
{0}
{self.optionals(optionals)}
else
{1}
{self.subcommands(subcommands)}
fi
""".format(
self.optionals(optionals), self.subcommands(subcommands)
)
"""
else:
return """
{0}
""".format(
self.optionals(optionals)
)
return f"""
{self.optionals(optionals)}
"""
def positionals(self, positionals: Sequence[str]) -> str:
"""Return the syntax for reporting positional arguments.
@@ -304,7 +300,7 @@ def optionals(self, optionals: Sequence[str]) -> str:
Returns:
Syntax for optional flags.
"""
return 'SPACK_COMPREPLY="{0}"'.format(" ".join(optionals))
return f'SPACK_COMPREPLY="{" ".join(optionals)}"'
def subcommands(self, subcommands: Sequence[str]) -> str:
"""Return the syntax for reporting subcommands.
@@ -315,7 +311,7 @@ def subcommands(self, subcommands: Sequence[str]) -> str:
Returns:
Syntax for subcommand parsers
"""
return 'SPACK_COMPREPLY="{0}"'.format(" ".join(subcommands))
return f'SPACK_COMPREPLY="{" ".join(subcommands)}"'
# Map argument destination names to their complete commands
@@ -395,7 +391,7 @@ def _fish_dest_get_complete(prog: str, dest: str) -> Optional[str]:
subcmd = s[1] if len(s) == 2 else ""
for (prog_key, pos_key), value in _dest_to_fish_complete.items():
if subcmd.startswith(prog_key) and re.match("^" + pos_key + "$", dest):
if subcmd.startswith(prog_key) and re.match(f"^{pos_key}$", dest):
return value
return None
@@ -427,24 +423,6 @@ def format(self, cmd: Command) -> str:
+ self.complete(cmd.prog, positionals, optionals, subcommands)
)
def _quote(self, string: str) -> str:
"""Quote string and escape special characters if necessary.
Args:
string: Input string.
Returns:
Quoted string.
"""
# Goal here is to match fish_indent behavior
# Strings without spaces (or other special characters) do not need to be escaped
if not any([sub in string for sub in [" ", "'", '"']]):
return string
string = string.replace("'", r"\'")
return f"'{string}'"
def optspecs(
self,
prog: str,
@@ -463,7 +441,7 @@ def optspecs(
optspec_var = "__fish_spack_optspecs_" + prog.replace(" ", "_").replace("-", "_")
if optionals is None:
return "set -g %s\n" % optspec_var
return f"set -g {optspec_var}\n"
# Build optspec by iterating over options
args = []
@@ -490,11 +468,11 @@ def optspecs(
long = [f[2:] for f in flags if f.startswith("--")]
while len(short) > 0 and len(long) > 0:
arg = "%s/%s%s" % (short.pop(), long.pop(), required)
arg = f"{short.pop()}/{long.pop()}{required}"
while len(short) > 0:
arg = "%s/%s" % (short.pop(), required)
arg = f"{short.pop()}/{required}"
while len(long) > 0:
arg = "%s%s" % (long.pop(), required)
arg = f"{long.pop()}{required}"
args.append(arg)
@@ -503,7 +481,7 @@ def optspecs(
# indicate that such subcommand exists.
args = " ".join(args)
return "set -g %s %s\n" % (optspec_var, args)
return f"set -g {optspec_var} {args}\n"
@staticmethod
def complete_head(
@@ -524,12 +502,14 @@ def complete_head(
subcmd = s[1] if len(s) == 2 else ""
if index is None:
return "complete -c %s -n '__fish_spack_using_command %s'" % (s[0], subcmd)
return f"complete -c {s[0]} -n '__fish_spack_using_command {subcmd}'"
elif nargs in [argparse.ZERO_OR_MORE, argparse.ONE_OR_MORE, argparse.REMAINDER]:
head = "complete -c %s -n '__fish_spack_using_command_pos_remainder %d %s'"
return (
f"complete -c {s[0]} -n '__fish_spack_using_command_pos_remainder "
f"{index} {subcmd}'"
)
else:
head = "complete -c %s -n '__fish_spack_using_command_pos %d %s'"
return head % (s[0], index, subcmd)
return f"complete -c {s[0]} -n '__fish_spack_using_command_pos {index} {subcmd}'"
def complete(
self,
@@ -597,25 +577,18 @@ def positionals(
if choices is not None:
# If there are choices, we provide a completion for all possible values.
commands.append(head + " -f -a %s" % self._quote(" ".join(choices)))
commands.append(f"{head} -f -a {shlex.quote(' '.join(choices))}")
else:
# Otherwise, we try to find a predefined completion for it
value = _fish_dest_get_complete(prog, args)
if value is not None:
commands.append(head + " " + value)
commands.append(f"{head} {value}")
return "\n".join(commands) + "\n"
def prog_comment(self, prog: str) -> str:
"""Return a comment line for the command.
Args:
prog: Program name.
Returns:
Comment line.
"""
return "\n# %s\n" % prog
"""Return a comment line for the command."""
return f"\n# {prog}\n"
def optionals(
self,
@@ -658,28 +631,28 @@ def optionals(
for f in flags:
if f.startswith("--"):
long = f[2:]
prefix += " -l %s" % long
prefix = f"{prefix} -l {long}"
elif f.startswith("-"):
short = f[1:]
assert len(short) == 1
prefix += " -s %s" % short
prefix = f"{prefix} -s {short}"
# Check if option require argument.
# Currently multi-argument options are not supported, so we treat it like one argument.
if nargs != 0:
prefix += " -r"
prefix = f"{prefix} -r"
if dest is not None:
# If there are choices, we provide a completion for all possible values.
commands.append(prefix + " -f -a %s" % self._quote(" ".join(dest)))
commands.append(f"{prefix} -f -a {shlex.quote(' '.join(dest))}")
else:
# Otherwise, we try to find a predefined completion for it
value = _fish_dest_get_complete(prog, dest)
if value is not None:
commands.append(prefix + " " + value)
commands.append(f"{prefix} {value}")
if help:
commands.append(prefix + " -d %s" % self._quote(help))
commands.append(f"{prefix} -d {shlex.quote(help)}")
return "\n".join(commands) + "\n"
@@ -697,11 +670,11 @@ def subcommands(self, prog: str, subcommands: List[Tuple[ArgumentParser, str, st
head = self.complete_head(prog, 0)
for _, subcommand, help in subcommands:
command = head + " -f -a %s" % self._quote(subcommand)
command = f"{head} -f -a {shlex.quote(subcommand)}"
if help is not None and len(help) > 0:
help = help.split("\n")[0]
command += " -d %s" % self._quote(help)
command = f"{command} -d {shlex.quote(help)}"
commands.append(command)
@@ -747,7 +720,7 @@ def rst_index(out: IO) -> None:
for i, cmd in enumerate(sorted(commands)):
description = description.capitalize() if i == 0 else ""
ref = ":ref:`%s <spack-%s>`" % (cmd, cmd)
ref = f":ref:`{cmd} <spack-{cmd}>`"
comma = "," if i != len(commands) - 1 else ""
bar = "| " if i % 8 == 0 else " "
out.write(line % (description, bar + ref + comma))
@@ -858,10 +831,10 @@ def _commands(parser: ArgumentParser, args: Namespace) -> None:
# check header first so we don't open out files unnecessarily
if args.header and not os.path.exists(args.header):
tty.die("No such file: '%s'" % args.header)
tty.die(f"No such file: '{args.header}'")
if args.update:
tty.msg("Updating file: %s" % args.update)
tty.msg(f"Updating file: {args.update}")
with open(args.update, "w") as f:
prepend_header(args, f)
formatter(args, f)

View File

@@ -50,6 +50,7 @@ def setup_parser(subparser):
default=lambda: spack.config.default_modify_scope("compilers"),
help="configuration scope to modify",
)
arguments.add_common_arguments(find_parser, ["jobs"])
# Remove
remove_parser = sp.add_parser("remove", aliases=["rm"], help="remove compiler by spec")
@@ -78,25 +79,21 @@ def setup_parser(subparser):
def compiler_find(args):
"""Search either $PATH or a list of paths OR MODULES for compilers and
add them to Spack's configuration.
"""
# None signals spack.compiler.find_compilers to use its default logic
paths = args.add_paths or None
# Below scope=None because we want new compilers that don't appear
# in any other configuration.
new_compilers = spack.compilers.find_new_compilers(
paths, scope=None, mixed_toolchain=args.mixed_toolchain
new_compilers = spack.compilers.find_compilers(
path_hints=paths,
scope=args.scope,
mixed_toolchain=args.mixed_toolchain,
max_workers=args.jobs,
)
if new_compilers:
spack.compilers.add_compilers_to_config(new_compilers, scope=args.scope)
n = len(new_compilers)
s = "s" if n > 1 else ""
config = spack.config.CONFIG
filename = config.get_config_filename(args.scope, "compilers")
tty.msg("Added %d new compiler%s to %s" % (n, s, filename))
colify(reversed(sorted(c.spec.display_str for c in new_compilers)), indent=4)
filename = spack.config.CONFIG.get_config_filename(args.scope, "compilers")
tty.msg(f"Added {n:d} new compiler{s} to {filename}")
compiler_strs = sorted(f"{c.spec.name}@{c.spec.version}" for c in new_compilers)
colify(reversed(compiler_strs), indent=4)
else:
tty.msg("Found no new compilers")
tty.msg("Compilers are defined in the following files:")

View File

@@ -6,6 +6,7 @@
import re
import sys
import urllib.parse
from typing import List
import llnl.util.tty as tty
from llnl.util.filesystem import mkdirp
@@ -14,9 +15,15 @@
import spack.stage
import spack.util.web
from spack.spec import Spec
from spack.url import UndetectableNameError, UndetectableVersionError, parse_name, parse_version
from spack.url import (
UndetectableNameError,
UndetectableVersionError,
find_versions_of_archive,
parse_name,
parse_version,
)
from spack.util.editor import editor
from spack.util.executable import ProcessError, which
from spack.util.executable import which
from spack.util.format import get_version_lines
from spack.util.naming import mod_to_class, simplify_name, valid_fully_qualified_module_name
@@ -89,14 +96,20 @@ class BundlePackageTemplate:
url_def = " # There is no URL since there is no code to download."
body_def = " # There is no need for install() since there is no code."
def __init__(self, name, versions):
def __init__(self, name: str, versions, languages: List[str]):
self.name = name
self.class_name = mod_to_class(name)
self.versions = versions
self.languages = languages
def write(self, pkg_path):
"""Writes the new package file."""
all_deps = [f' depends_on("{lang}", type="build")' for lang in self.languages]
if all_deps and self.dependencies:
all_deps.append("")
all_deps.append(self.dependencies)
# Write out a template for the file
with open(pkg_path, "w") as pkg_file:
pkg_file.write(
@@ -106,7 +119,7 @@ def write(self, pkg_path):
base_class_name=self.base_class_name,
url_def=self.url_def,
versions=self.versions,
dependencies=self.dependencies,
dependencies="\n".join(all_deps),
body_def=self.body_def,
)
)
@@ -125,8 +138,8 @@ def install(self, spec, prefix):
url_line = ' url = "{url}"'
def __init__(self, name, url, versions):
super().__init__(name, versions)
def __init__(self, name, url, versions, languages: List[str]):
super().__init__(name, versions, languages)
self.url_def = self.url_line.format(url=url)
@@ -214,13 +227,13 @@ def luarocks_args(self):
args = []
return args"""
def __init__(self, name, url, *args, **kwargs):
def __init__(self, name, url, versions, languages: List[str]):
# If the user provided `--name lua-lpeg`, don't rename it lua-lua-lpeg
if not name.startswith("lua-"):
# Make it more obvious that we are renaming the package
tty.msg("Changing package name from {0} to lua-{0}".format(name))
name = "lua-{0}".format(name)
super().__init__(name, url, *args, **kwargs)
super().__init__(name, url, versions, languages)
class MesonPackageTemplate(PackageTemplate):
@@ -321,14 +334,14 @@ class RacketPackageTemplate(PackageTemplate):
# subdirectory = None
"""
def __init__(self, name, url, *args, **kwargs):
def __init__(self, name, url, versions, languages: List[str]):
# If the user provided `--name rkt-scribble`, don't rename it rkt-rkt-scribble
if not name.startswith("rkt-"):
# Make it more obvious that we are renaming the package
tty.msg("Changing package name from {0} to rkt-{0}".format(name))
name = "rkt-{0}".format(name)
self.body_def = self.body_def.format(name[4:])
super().__init__(name, url, *args, **kwargs)
super().__init__(name, url, versions, languages)
class PythonPackageTemplate(PackageTemplate):
@@ -361,7 +374,7 @@ def config_settings(self, spec, prefix):
settings = {}
return settings"""
def __init__(self, name, url, *args, **kwargs):
def __init__(self, name, url, versions, languages: List[str]):
# If the user provided `--name py-numpy`, don't rename it py-py-numpy
if not name.startswith("py-"):
# Make it more obvious that we are renaming the package
@@ -415,7 +428,7 @@ def __init__(self, name, url, *args, **kwargs):
+ self.url_line
)
super().__init__(name, url, *args, **kwargs)
super().__init__(name, url, versions, languages)
class RPackageTemplate(PackageTemplate):
@@ -434,7 +447,7 @@ def configure_args(self):
args = []
return args"""
def __init__(self, name, url, *args, **kwargs):
def __init__(self, name, url, versions, languages: List[str]):
# If the user provided `--name r-rcpp`, don't rename it r-r-rcpp
if not name.startswith("r-"):
# Make it more obvious that we are renaming the package
@@ -454,7 +467,7 @@ def __init__(self, name, url, *args, **kwargs):
if bioc:
self.url_line = ' url = "{0}"\n' ' bioc = "{1}"'.format(url, r_name)
super().__init__(name, url, *args, **kwargs)
super().__init__(name, url, versions, languages)
class PerlmakePackageTemplate(PackageTemplate):
@@ -474,14 +487,14 @@ def configure_args(self):
args = []
return args"""
def __init__(self, name, *args, **kwargs):
def __init__(self, name, url, versions, languages: List[str]):
# If the user provided `--name perl-cpp`, don't rename it perl-perl-cpp
if not name.startswith("perl-"):
# Make it more obvious that we are renaming the package
tty.msg("Changing package name from {0} to perl-{0}".format(name))
name = "perl-{0}".format(name)
super().__init__(name, *args, **kwargs)
super().__init__(name, url, versions, languages)
class PerlbuildPackageTemplate(PerlmakePackageTemplate):
@@ -506,7 +519,7 @@ class OctavePackageTemplate(PackageTemplate):
# FIXME: Add additional dependencies if required.
# depends_on("octave-foo", type=("build", "run"))"""
def __init__(self, name, *args, **kwargs):
def __init__(self, name, url, versions, languages: List[str]):
# If the user provided `--name octave-splines`, don't rename it
# octave-octave-splines
if not name.startswith("octave-"):
@@ -514,7 +527,7 @@ def __init__(self, name, *args, **kwargs):
tty.msg("Changing package name from {0} to octave-{0}".format(name))
name = "octave-{0}".format(name)
super().__init__(name, *args, **kwargs)
super().__init__(name, url, versions, languages)
class RubyPackageTemplate(PackageTemplate):
@@ -534,7 +547,7 @@ def build(self, spec, prefix):
# FIXME: If not needed delete this function
pass"""
def __init__(self, name, *args, **kwargs):
def __init__(self, name, url, versions, languages: List[str]):
# If the user provided `--name ruby-numpy`, don't rename it
# ruby-ruby-numpy
if not name.startswith("ruby-"):
@@ -542,7 +555,7 @@ def __init__(self, name, *args, **kwargs):
tty.msg("Changing package name from {0} to ruby-{0}".format(name))
name = "ruby-{0}".format(name)
super().__init__(name, *args, **kwargs)
super().__init__(name, url, versions, languages)
class MakefilePackageTemplate(PackageTemplate):
@@ -580,14 +593,14 @@ def configure_args(self, spec, prefix):
args = []
return args"""
def __init__(self, name, *args, **kwargs):
def __init__(self, name, url, versions, languages: List[str]):
# If the user provided `--name py-pyqt4`, don't rename it py-py-pyqt4
if not name.startswith("py-"):
# Make it more obvious that we are renaming the package
tty.msg("Changing package name from {0} to py-{0}".format(name))
name = "py-{0}".format(name)
super().__init__(name, *args, **kwargs)
super().__init__(name, url, versions, languages)
templates = {
@@ -658,8 +671,48 @@ def setup_parser(subparser):
)
class BuildSystemGuesser:
"""An instance of BuildSystemGuesser provides a callable object to be used
#: C file extensions
C_EXT = {".c"}
#: C++ file extensions
CXX_EXT = {
".C",
".c++",
".cc",
".ccm",
".cpp",
".CPP",
".cxx",
".h++",
".hh",
".hpp",
".hxx",
".inl",
".ipp",
".ixx",
".tcc",
".tpp",
}
#: Fortran file extensions
FORTRAN_EXT = {
".f77",
".F77",
".f90",
".F90",
".f95",
".F95",
".f",
".F",
".for",
".FOR",
".ftn",
".FTN",
}
class BuildSystemAndLanguageGuesser:
"""An instance of BuildSystemAndLanguageGuesser provides a callable object to be used
during ``spack create``. By passing this object to ``spack checksum``, we
can take a peek at the fetched tarball and discern the build system it uses
"""
@@ -667,81 +720,119 @@ class BuildSystemGuesser:
def __init__(self):
"""Sets the default build system."""
self.build_system = "generic"
self._c = False
self._cxx = False
self._fortran = False
def __call__(self, stage, url):
# List of files in the archive ordered by their depth in the directory tree.
self._file_entries: List[str] = []
def __call__(self, archive: str, url: str) -> None:
"""Try to guess the type of build system used by a project based on
the contents of its archive or the URL it was downloaded from."""
if url is not None:
# Most octave extensions are hosted on Octave-Forge:
# https://octave.sourceforge.net/index.html
# They all have the same base URL.
if "downloads.sourceforge.net/octave/" in url:
self.build_system = "octave"
return
if url.endswith(".gem"):
self.build_system = "ruby"
return
if url.endswith(".whl") or ".whl#" in url:
self.build_system = "python"
return
if url.endswith(".rock"):
self.build_system = "lua"
return
# A list of clues that give us an idea of the build system a package
# uses. If the regular expression matches a file contained in the
# archive, the corresponding build system is assumed.
# NOTE: Order is important here. If a package supports multiple
# build systems, we choose the first match in this list.
clues = [
(r"/CMakeLists\.txt$", "cmake"),
(r"/NAMESPACE$", "r"),
(r"/Cargo\.toml$", "cargo"),
(r"/go\.mod$", "go"),
(r"/configure$", "autotools"),
(r"/configure\.(in|ac)$", "autoreconf"),
(r"/Makefile\.am$", "autoreconf"),
(r"/pom\.xml$", "maven"),
(r"/SConstruct$", "scons"),
(r"/waf$", "waf"),
(r"/pyproject.toml", "python"),
(r"/setup\.(py|cfg)$", "python"),
(r"/WORKSPACE$", "bazel"),
(r"/Build\.PL$", "perlbuild"),
(r"/Makefile\.PL$", "perlmake"),
(r"/.*\.gemspec$", "ruby"),
(r"/Rakefile$", "ruby"),
(r"/setup\.rb$", "ruby"),
(r"/.*\.pro$", "qmake"),
(r"/.*\.rockspec$", "lua"),
(r"/(GNU)?[Mm]akefile$", "makefile"),
(r"/DESCRIPTION$", "octave"),
(r"/meson\.build$", "meson"),
(r"/configure\.py$", "sip"),
]
# Peek inside the compressed file.
if stage.archive_file.endswith(".zip") or ".zip#" in stage.archive_file:
if archive.endswith(".zip") or ".zip#" in archive:
try:
unzip = which("unzip")
output = unzip("-lq", stage.archive_file, output=str)
except ProcessError:
assert unzip is not None
output = unzip("-lq", archive, output=str)
except Exception:
output = ""
else:
try:
tar = which("tar")
output = tar("--exclude=*/*/*", "-tf", stage.archive_file, output=str)
except ProcessError:
assert tar is not None
output = tar("tf", archive, output=str)
except Exception:
output = ""
lines = output.splitlines()
self._file_entries[:] = output.splitlines()
# Determine the build system based on the files contained
# in the archive.
for pattern, bs in clues:
if any(re.search(pattern, line) for line in lines):
self.build_system = bs
break
# Files closest to the root should be considered first when determining build system.
self._file_entries.sort(key=lambda p: p.count("/"))
self._determine_build_system(url)
self._determine_language()
def _determine_build_system(self, url: str) -> None:
# Most octave extensions are hosted on Octave-Forge:
# https://octave.sourceforge.net/index.html
# They all have the same base URL.
if "downloads.sourceforge.net/octave/" in url:
self.build_system = "octave"
elif url.endswith(".gem"):
self.build_system = "ruby"
elif url.endswith(".whl") or ".whl#" in url:
self.build_system = "python"
elif url.endswith(".rock"):
self.build_system = "lua"
elif self._file_entries:
# A list of clues that give us an idea of the build system a package
# uses. If the regular expression matches a file contained in the
# archive, the corresponding build system is assumed.
# NOTE: Order is important here. If a package supports multiple
# build systems, we choose the first match in this list.
clues = [
(re.compile(pattern), build_system)
for pattern, build_system in (
(r"/CMakeLists\.txt$", "cmake"),
(r"/NAMESPACE$", "r"),
(r"/Cargo\.toml$", "cargo"),
(r"/go\.mod$", "go"),
(r"/configure$", "autotools"),
(r"/configure\.(in|ac)$", "autoreconf"),
(r"/Makefile\.am$", "autoreconf"),
(r"/pom\.xml$", "maven"),
(r"/SConstruct$", "scons"),
(r"/waf$", "waf"),
(r"/pyproject.toml", "python"),
(r"/setup\.(py|cfg)$", "python"),
(r"/WORKSPACE$", "bazel"),
(r"/Build\.PL$", "perlbuild"),
(r"/Makefile\.PL$", "perlmake"),
(r"/.*\.gemspec$", "ruby"),
(r"/Rakefile$", "ruby"),
(r"/setup\.rb$", "ruby"),
(r"/.*\.pro$", "qmake"),
(r"/.*\.rockspec$", "lua"),
(r"/(GNU)?[Mm]akefile$", "makefile"),
(r"/DESCRIPTION$", "octave"),
(r"/meson\.build$", "meson"),
(r"/configure\.py$", "sip"),
)
]
# Determine the build system based on the files contained in the archive.
for file in self._file_entries:
for pattern, build_system in clues:
if pattern.search(file):
self.build_system = build_system
return
def _determine_language(self):
for entry in self._file_entries:
_, ext = os.path.splitext(entry)
if not self._c and ext in C_EXT:
self._c = True
elif not self._cxx and ext in CXX_EXT:
self._cxx = True
elif not self._fortran and ext in FORTRAN_EXT:
self._fortran = True
if self._c and self._cxx and self._fortran:
return
@property
def languages(self) -> List[str]:
langs: List[str] = []
if self._c:
langs.append("c")
if self._cxx:
langs.append("cxx")
if self._fortran:
langs.append("fortran")
return langs
def get_name(name, url):
@@ -811,7 +902,7 @@ def get_url(url):
def get_versions(args, name):
"""Returns a list of versions and hashes for a package.
Also returns a BuildSystemGuesser object.
Also returns a BuildSystemAndLanguageGuesser object.
Returns default values if no URL is provided.
@@ -820,7 +911,7 @@ def get_versions(args, name):
name (str): The name of the package
Returns:
tuple: versions and hashes, and a BuildSystemGuesser object
tuple: versions and hashes, and a BuildSystemAndLanguageGuesser object
"""
# Default version with hash
@@ -834,7 +925,7 @@ def get_versions(args, name):
# version("1.2.4")"""
# Default guesser
guesser = BuildSystemGuesser()
guesser = BuildSystemAndLanguageGuesser()
valid_url = True
try:
@@ -847,7 +938,7 @@ def get_versions(args, name):
if args.url is not None and args.template != "bundle" and valid_url:
# Find available versions
try:
url_dict = spack.url.find_versions_of_archive(args.url)
url_dict = find_versions_of_archive(args.url)
if len(url_dict) > 1 and not args.batch and sys.stdin.isatty():
url_dict_filtered = spack.stage.interactive_version_filter(url_dict)
if url_dict_filtered is None:
@@ -874,7 +965,7 @@ def get_versions(args, name):
return versions, guesser
def get_build_system(template, url, guesser):
def get_build_system(template: str, url: str, guesser: BuildSystemAndLanguageGuesser) -> str:
"""Determine the build system template.
If a template is specified, always use that. Otherwise, if a URL
@@ -882,11 +973,10 @@ def get_build_system(template, url, guesser):
build system it uses. Otherwise, use a generic template by default.
Args:
template (str): ``--template`` argument given to ``spack create``
url (str): ``url`` argument given to ``spack create``
args (argparse.Namespace): The arguments given to ``spack create``
guesser (BuildSystemGuesser): The first_stage_function given to
``spack checksum`` which records the build system it detects
template: ``--template`` argument given to ``spack create``
url: ``url`` argument given to ``spack create``
guesser: The first_stage_function given to ``spack checksum`` which records the build
system it detects
Returns:
str: The name of the build system template to use
@@ -960,7 +1050,7 @@ def create(parser, args):
build_system = get_build_system(args.template, url, guesser)
# Create the package template object
constr_args = {"name": name, "versions": versions}
constr_args = {"name": name, "versions": versions, "languages": guesser.languages}
package_class = templates[build_system]
if package_class != BundlePackageTemplate:
constr_args["url"] = url

View File

@@ -6,6 +6,7 @@
import os
import platform
import re
import sys
from datetime import datetime
from glob import glob
@@ -62,9 +63,10 @@ def create_db_tarball(args):
base = os.path.basename(str(spack.store.STORE.root))
transform_args = []
# Currently --transform and -s are not supported by Windows native tar
if "GNU" in tar("--version", output=str):
transform_args = ["--transform", "s/^%s/%s/" % (base, tarball_name)]
else:
elif sys.platform != "win32":
transform_args = ["-s", "/^%s/%s/" % (base, tarball_name)]
wd = os.path.dirname(str(spack.store.STORE.root))
@@ -90,7 +92,6 @@ def report(args):
print("* **Spack:**", get_version())
print("* **Python:**", platform.python_version())
print("* **Platform:**", architecture)
print("* **Concretizer:**", spack.config.get("config:concretizer"))
def debug(parser, args):

View File

@@ -14,7 +14,6 @@
installation and its deprecator.
"""
import argparse
import os
import llnl.util.tty as tty
from llnl.util.symlink import symlink
@@ -76,12 +75,7 @@ def setup_parser(sp):
)
sp.add_argument(
"-l",
"--link-type",
type=str,
default="soft",
choices=["soft", "hard"],
help="type of filesystem link to use for deprecation (default soft)",
"-l", "--link-type", type=str, default=None, choices=["soft", "hard"], help="(deprecated)"
)
sp.add_argument(
@@ -91,6 +85,9 @@ def setup_parser(sp):
def deprecate(parser, args):
"""Deprecate one spec in favor of another"""
if args.link_type is not None:
tty.warn("The --link-type option is deprecated and will be removed in a future release.")
env = ev.active_environment()
specs = spack.cmd.parse_specs(args.specs)
@@ -144,7 +141,5 @@ def deprecate(parser, args):
if not answer:
tty.die("Will not deprecate any packages.")
link_fn = os.link if args.link_type == "hard" else symlink
for dcate, dcator in zip(all_deprecate, all_deprecators):
dcate.package.do_deprecate(dcator, link_fn)
dcate.package.do_deprecate(dcator, symlink)

View File

@@ -10,8 +10,10 @@
import spack.cmd
import spack.config
import spack.fetch_strategy
import spack.package_base
import spack.repo
import spack.spec
import spack.stage
import spack.util.path
import spack.version
from spack.cmd.common import arguments
@@ -62,7 +64,7 @@ def change_fn(section):
spack.config.change_or_add("develop", find_fn, change_fn)
def _retrieve_develop_source(spec, abspath):
def _retrieve_develop_source(spec: spack.spec.Spec, abspath: str) -> None:
# "steal" the source code via staging API. We ask for a stage
# to be created, then copy it afterwards somewhere else. It would be
# better if we can create the `source_path` directly into its final
@@ -71,13 +73,13 @@ def _retrieve_develop_source(spec, abspath):
# We construct a package class ourselves, rather than asking for
# Spec.package, since Spec only allows this when it is concrete
package = pkg_cls(spec)
source_stage = package.stage[0]
source_stage: spack.stage.Stage = package.stage[0]
if isinstance(source_stage.fetcher, spack.fetch_strategy.GitFetchStrategy):
source_stage.fetcher.get_full_repo = True
# If we retrieved this version before and cached it, we may have
# done so without cloning the full git repo; likewise, any
# mirror might store an instance with truncated history.
source_stage.disable_mirrors()
source_stage.default_fetcher_only = True
source_stage.fetcher.set_package(package)
package.stage.steal_source(abspath)

View File

@@ -468,32 +468,30 @@ def env_remove(args):
This removes an environment managed by Spack. Directory environments
and manifests embedded in repositories should be removed manually.
"""
read_envs = []
remove_envs = []
valid_envs = []
bad_envs = []
invalid_envs = []
for env_name in ev.all_environment_names():
try:
env = ev.read(env_name)
valid_envs.append(env_name)
valid_envs.append(env)
if env_name in args.rm_env:
read_envs.append(env)
remove_envs.append(env)
except (spack.config.ConfigFormatError, ev.SpackEnvironmentConfigError):
invalid_envs.append(env_name)
if env_name in args.rm_env:
bad_envs.append(env_name)
# Check if env is linked to another before trying to remove
for name in valid_envs:
# Check if remove_env is included from another env before trying to remove
for env in valid_envs:
for remove_env in remove_envs:
# don't check if environment is included to itself
if name == env_name:
if env.name == remove_env.name:
continue
environ = ev.Environment(ev.root(name))
if ev.root(env_name) in environ.included_concrete_envs:
msg = f'Environment "{env_name}" is being used by environment "{name}"'
if remove_env.path in env.included_concrete_envs:
msg = f'Environment "{remove_env.name}" is being used by environment "{env.name}"'
if args.force:
tty.warn(msg)
else:
@@ -506,7 +504,7 @@ def env_remove(args):
if not answer:
tty.die("Will not remove any environments")
for env in read_envs:
for env in remove_envs:
name = env.name
if env.active:
tty.die(f"Environment {name} can't be removed while activated.")

View File

@@ -5,10 +5,12 @@
import argparse
import os
import tempfile
import spack.binary_distribution
import spack.mirror
import spack.paths
import spack.stage
import spack.util.gpg
import spack.util.url
from spack.cmd.common import arguments
@@ -115,6 +117,7 @@ def setup_parser(subparser):
help="URL of the mirror where keys will be published",
)
publish.add_argument(
"--update-index",
"--rebuild-index",
action="store_true",
default=False,
@@ -220,9 +223,10 @@ def gpg_publish(args):
elif args.mirror_url:
mirror = spack.mirror.Mirror(args.mirror_url, args.mirror_url)
spack.binary_distribution.push_keys(
mirror, keys=args.keys, regenerate_index=args.rebuild_index
)
with tempfile.TemporaryDirectory(dir=spack.stage.get_stage_root()) as tmpdir:
spack.binary_distribution._url_push_keys(
mirror, keys=args.keys, tmpdir=tmpdir, update_index=args.update_index
)
def gpg(parser, args):

View File

@@ -48,6 +48,7 @@ def setup_parser(subparser):
options = [
("--detectable", print_detectable.__doc__),
("--maintainers", print_maintainers.__doc__),
("--namespace", print_namespace.__doc__),
("--no-dependencies", "do not " + print_dependencies.__doc__),
("--no-variants", "do not " + print_variants.__doc__),
("--no-versions", "do not " + print_versions.__doc__),
@@ -189,6 +190,15 @@ def print_maintainers(pkg, args):
color.cprint(section_title("Maintainers: ") + mnt)
def print_namespace(pkg, args):
"""output package namespace"""
repo = spack.repo.PATH.get_repo(pkg.namespace)
color.cprint("")
color.cprint(section_title("Namespace:"))
color.cprint(f" @c{{{repo.namespace}}} at {repo.root}")
def print_phases(pkg, args):
"""output installation phases"""
@@ -502,7 +512,7 @@ def print_licenses(pkg, args):
def info(parser, args):
spec = spack.spec.Spec(args.package)
pkg_cls = spack.repo.PATH.get_pkg_class(spec.name)
pkg_cls = spack.repo.PATH.get_pkg_class(spec.fullname)
pkg = pkg_cls(spec)
# Output core package information
@@ -522,6 +532,7 @@ def info(parser, args):
# Now output optional information in expected order
sections = [
(args.all or args.maintainers, print_maintainers),
(args.all or args.namespace, print_namespace),
(args.all or args.detectable, print_detectable),
(args.all or args.tags, print_tags),
(args.all or not args.no_versions, print_versions),

View File

@@ -23,11 +23,6 @@ def setup_parser(subparser):
output.add_argument(
"-s", "--safe", action="store_true", help="only list safe versions of the package"
)
output.add_argument(
"--safe-only",
action="store_true",
help="[deprecated] only list safe versions of the package",
)
output.add_argument(
"-r", "--remote", action="store_true", help="only list remote versions of the package"
)
@@ -47,17 +42,13 @@ def versions(parser, args):
safe_versions = pkg.versions
if args.safe_only:
tty.warn('"--safe-only" is deprecated. Use "--safe" instead.')
args.safe = args.safe_only
if not (args.remote or args.new):
if sys.stdout.isatty():
tty.msg("Safe versions (already checksummed):")
if not safe_versions:
if sys.stdout.isatty():
tty.warn("Found no versions for {0}".format(pkg.name))
tty.warn(f"Found no versions for {pkg.name}")
tty.debug("Manually add versions to the package.")
else:
colify(sorted(safe_versions, reverse=True), indent=2)
@@ -83,12 +74,12 @@ def versions(parser, args):
if not remote_versions:
if sys.stdout.isatty():
if not fetched_versions:
tty.warn("Found no versions for {0}".format(pkg.name))
tty.warn(f"Found no versions for {pkg.name}")
tty.debug(
"Check the list_url and list_depth attributes of "
"the package to help Spack find versions."
)
else:
tty.warn("Found no unchecksummed versions for {0}".format(pkg.name))
tty.warn(f"Found no unchecksummed versions for {pkg.name}")
else:
colify(sorted(remote_versions, reverse=True), indent=2)

View File

@@ -29,6 +29,9 @@
__all__ = ["Compiler"]
PATH_INSTANCE_VARS = ["cc", "cxx", "f77", "fc"]
FLAG_INSTANCE_VARS = ["cflags", "cppflags", "cxxflags", "fflags"]
@llnl.util.lang.memoized
def _get_compiler_version_output(compiler_path, version_arg, ignore_errors=()):
@@ -700,6 +703,30 @@ def compiler_environment(self):
os.environ.clear()
os.environ.update(backup_env)
def to_dict(self):
flags_dict = {fname: " ".join(fvals) for fname, fvals in self.flags.items()}
flags_dict.update(
{attr: getattr(self, attr, None) for attr in FLAG_INSTANCE_VARS if hasattr(self, attr)}
)
result = {
"spec": str(self.spec),
"paths": {attr: getattr(self, attr, None) for attr in PATH_INSTANCE_VARS},
"flags": flags_dict,
"operating_system": str(self.operating_system),
"target": str(self.target),
"modules": self.modules or [],
"environment": self.environment or {},
"extra_rpaths": self.extra_rpaths or [],
}
if self.enable_implicit_rpaths is not None:
result["implicit_rpaths"] = self.enable_implicit_rpaths
if self.alias:
result["alias"] = self.alias
return result
class CompilerAccessError(spack.error.SpackError):
def __init__(self, compiler, paths):

View File

@@ -6,12 +6,11 @@
"""This module contains functions related to finding compilers on the
system and configuring Spack to use multiple compilers.
"""
import collections
import itertools
import multiprocessing.pool
import importlib
import os
import sys
import warnings
from typing import Dict, List, Optional, Tuple
from typing import Dict, List, Optional
import archspec.cpu
@@ -22,16 +21,15 @@
import spack.compiler
import spack.config
import spack.error
import spack.operating_systems
import spack.paths
import spack.platforms
import spack.repo
import spack.spec
import spack.version
from spack.operating_systems import windows_os
from spack.util.environment import get_path
from spack.util.naming import mod_to_class
_path_instance_vars = ["cc", "cxx", "f77", "fc"]
_flags_instance_vars = ["cflags", "cppflags", "cxxflags", "fflags"]
_other_instance_vars = [
"modules",
"operating_system",
@@ -63,6 +61,10 @@
}
#: Tag used to identify packages providing a compiler
COMPILER_TAG = "compiler"
def pkg_spec_for_compiler(cspec):
"""Return the spec of the package that provides the compiler."""
for spec, package in _compiler_to_pkg.items():
@@ -85,29 +87,7 @@ def converter(cspec_like, *args, **kwargs):
def _to_dict(compiler):
"""Return a dict version of compiler suitable to insert in YAML."""
d = {}
d["spec"] = str(compiler.spec)
d["paths"] = dict((attr, getattr(compiler, attr, None)) for attr in _path_instance_vars)
d["flags"] = dict((fname, " ".join(fvals)) for fname, fvals in compiler.flags.items())
d["flags"].update(
dict(
(attr, getattr(compiler, attr, None))
for attr in _flags_instance_vars
if hasattr(compiler, attr)
)
)
d["operating_system"] = str(compiler.operating_system)
d["target"] = str(compiler.target)
d["modules"] = compiler.modules or []
d["environment"] = compiler.environment or {}
d["extra_rpaths"] = compiler.extra_rpaths or []
if compiler.enable_implicit_rpaths is not None:
d["implicit_rpaths"] = compiler.enable_implicit_rpaths
if compiler.alias:
d["alias"] = compiler.alias
return {"compiler": d}
return {"compiler": compiler.to_dict()}
def get_compiler_config(
@@ -127,7 +107,7 @@ def get_compiler_config(
# Do not init config because there is a non-empty scope
return config
_init_compiler_config(configuration, scope=scope)
find_compilers(scope=scope)
config = configuration.get("compilers", scope=scope)
return config
@@ -136,125 +116,8 @@ def get_compiler_config_from_packages(
configuration: "spack.config.Configuration", *, scope: Optional[str] = None
) -> List[Dict]:
"""Return the compiler configuration from packages.yaml"""
config = configuration.get("packages", scope=scope)
if not config:
return []
packages = []
compiler_package_names = supported_compilers() + list(package_name_to_compiler_name.keys())
for name, entry in config.items():
if name not in compiler_package_names:
continue
externals_config = entry.get("externals", None)
if not externals_config:
continue
packages.extend(_compiler_config_from_package_config(externals_config))
return packages
def _compiler_config_from_package_config(config):
compilers = []
for entry in config:
compiler = _compiler_config_from_external(entry)
if compiler:
compilers.append(compiler)
return compilers
def _compiler_config_from_external(config):
extra_attributes_key = "extra_attributes"
compilers_key = "compilers"
c_key, cxx_key, fortran_key = "c", "cxx", "fortran"
# Allow `@x.y.z` instead of `@=x.y.z`
spec = spack.spec.parse_with_version_concrete(config["spec"])
compiler_spec = spack.spec.CompilerSpec(
package_name_to_compiler_name.get(spec.name, spec.name), spec.version
)
err_header = f"The external spec '{spec}' cannot be used as a compiler"
# If extra_attributes is not there I might not want to use this entry as a compiler,
# therefore just leave a debug message, but don't be loud with a warning.
if extra_attributes_key not in config:
tty.debug(f"[{__file__}] {err_header}: missing the '{extra_attributes_key}' key")
return None
extra_attributes = config[extra_attributes_key]
# If I have 'extra_attributes' warn if 'compilers' is missing, or we don't have a C compiler
if compilers_key not in extra_attributes:
warnings.warn(
f"{err_header}: missing the '{compilers_key}' key under '{extra_attributes_key}'"
)
return None
attribute_compilers = extra_attributes[compilers_key]
if c_key not in attribute_compilers:
warnings.warn(
f"{err_header}: missing the C compiler path under "
f"'{extra_attributes_key}:{compilers_key}'"
)
return None
c_compiler = attribute_compilers[c_key]
# C++ and Fortran compilers are not mandatory, so let's just leave a debug trace
if cxx_key not in attribute_compilers:
tty.debug(f"[{__file__}] The external spec {spec} does not have a C++ compiler")
if fortran_key not in attribute_compilers:
tty.debug(f"[{__file__}] The external spec {spec} does not have a Fortran compiler")
# compilers format has cc/fc/f77, externals format has "c/fortran"
paths = {
"cc": c_compiler,
"cxx": attribute_compilers.get(cxx_key, None),
"fc": attribute_compilers.get(fortran_key, None),
"f77": attribute_compilers.get(fortran_key, None),
}
if not spec.architecture:
host_platform = spack.platforms.host()
operating_system = host_platform.operating_system("default_os")
target = host_platform.target("default_target").microarchitecture
else:
target = spec.architecture.target
if not target:
target = spack.platforms.host().target("default_target")
target = target.microarchitecture
operating_system = spec.os
if not operating_system:
host_platform = spack.platforms.host()
operating_system = host_platform.operating_system("default_os")
compiler_entry = {
"compiler": {
"spec": str(compiler_spec),
"paths": paths,
"flags": extra_attributes.get("flags", {}),
"operating_system": str(operating_system),
"target": str(target.family),
"modules": config.get("modules", []),
"environment": extra_attributes.get("environment", {}),
"extra_rpaths": extra_attributes.get("extra_rpaths", []),
"implicit_rpaths": extra_attributes.get("implicit_rpaths", None),
}
}
return compiler_entry
def _init_compiler_config(
configuration: "spack.config.Configuration", *, scope: Optional[str]
) -> None:
"""Compiler search used when Spack has no compilers."""
compilers = find_compilers()
compilers_dict = []
for compiler in compilers:
compilers_dict.append(_to_dict(compiler))
configuration.set("compilers", compilers_dict, scope=scope)
packages_yaml = configuration.get("packages", scope=scope)
return CompilerConfigFactory.from_packages_yaml(packages_yaml)
def compiler_config_files():
@@ -278,9 +141,7 @@ def add_compilers_to_config(compilers, scope=None):
compilers: a list of Compiler objects.
scope: configuration scope to modify.
"""
compiler_config = get_compiler_config(
configuration=spack.config.CONFIG, scope=scope, init_config=False
)
compiler_config = get_compiler_config(configuration=spack.config.CONFIG, scope=scope)
for compiler in compilers:
if not compiler.cc:
tty.debug(f"{compiler.spec} does not have a C compiler")
@@ -329,9 +190,7 @@ def _remove_compiler_from_scope(compiler_spec, scope):
True if one or more compiler entries were actually removed, False otherwise
"""
assert scope is not None, "a specific scope is needed when calling this function"
compiler_config = get_compiler_config(
configuration=spack.config.CONFIG, scope=scope, init_config=False
)
compiler_config = get_compiler_config(configuration=spack.config.CONFIG, scope=scope)
filtered_compiler_config = [
compiler_entry
for compiler_entry in compiler_config
@@ -380,79 +239,77 @@ def all_compiler_specs(scope=None, init_config=True):
def find_compilers(
path_hints: Optional[List[str]] = None, *, mixed_toolchain=False
path_hints: Optional[List[str]] = None,
*,
scope: Optional[str] = None,
mixed_toolchain: bool = False,
max_workers: Optional[int] = None,
) -> List["spack.compiler.Compiler"]:
"""Return the list of compilers found in the paths given as arguments.
"""Searches for compiler in the paths given as argument. If any new compiler is found, the
configuration is updated, and the list of new compiler objects is returned.
Args:
path_hints: list of path hints where to look for. A sensible default based on the ``PATH``
environment variable will be used if the value is None
scope: configuration scope to modify
mixed_toolchain: allow mixing compilers from different toolchains if otherwise missing for
a certain language
max_workers: number of processes used to search for compilers
"""
import spack.detection
known_compilers = set(all_compilers(init_config=False))
if path_hints is None:
path_hints = get_path("PATH")
default_paths = fs.search_paths_for_executables(*path_hints)
if sys.platform == "win32":
default_paths.extend(windows_os.WindowsOs().compiler_search_paths)
compiler_pkgs = spack.repo.PATH.packages_with_tags(COMPILER_TAG, full=True)
# To detect the version of the compilers, we dispatch a certain number
# of function calls to different workers. Here we construct the list
# of arguments for each call.
arguments = []
for o in all_os_classes():
search_paths = getattr(o, "compiler_search_paths", default_paths)
arguments.extend(arguments_to_detect_version_fn(o, search_paths))
# Here we map the function arguments to the corresponding calls
tp = multiprocessing.pool.ThreadPool()
try:
detected_versions = tp.map(detect_version, arguments)
finally:
tp.close()
def valid_version(item: Tuple[Optional[DetectVersionArgs], Optional[str]]) -> bool:
value, error = item
if error is None:
return True
try:
# This will fail on Python 2.6 if a non ascii
# character is in the error
tty.debug(error)
except UnicodeEncodeError:
pass
return False
def remove_errors(
item: Tuple[Optional[DetectVersionArgs], Optional[str]]
) -> DetectVersionArgs:
value, _ = item
assert value is not None
return value
return make_compiler_list(
[remove_errors(detected) for detected in detected_versions if valid_version(detected)],
mixed_toolchain=mixed_toolchain,
detected_packages = spack.detection.by_path(
compiler_pkgs, path_hints=default_paths, max_workers=max_workers
)
valid_compilers = {}
for name, detected in detected_packages.items():
compilers = [x for x in detected if CompilerConfigFactory.from_external_spec(x)]
if not compilers:
continue
valid_compilers[name] = compilers
def find_new_compilers(
path_hints: Optional[List[str]] = None,
scope: Optional[str] = None,
*,
mixed_toolchain: bool = False,
):
"""Same as ``find_compilers`` but return only the compilers that are not
already in compilers.yaml.
def _has_fortran_compilers(x):
if "compilers" not in x.extra_attributes:
return False
Args:
path_hints: list of path hints where to look for. A sensible default based on the ``PATH``
environment variable will be used if the value is None
scope: scope to look for a compiler. If None consider the merged configuration.
mixed_toolchain: allow mixing compilers from different toolchains if otherwise missing for
a certain language
"""
compilers = find_compilers(path_hints, mixed_toolchain=mixed_toolchain)
return "fortran" in x.extra_attributes["compilers"]
return select_new_compilers(compilers, scope)
if mixed_toolchain:
gccs = [x for x in valid_compilers.get("gcc", []) if _has_fortran_compilers(x)]
if gccs:
best_gcc = sorted(
gccs, key=lambda x: spack.spec.parse_with_version_concrete(x).version
)[-1]
gfortran = best_gcc.extra_attributes["compilers"]["fortran"]
for name in ("llvm", "apple-clang"):
if name not in valid_compilers:
continue
candidates = valid_compilers[name]
for candidate in candidates:
if _has_fortran_compilers(candidate):
continue
candidate.extra_attributes["compilers"]["fortran"] = gfortran
new_compilers = []
for name, detected in valid_compilers.items():
for config in CompilerConfigFactory.from_specs(detected):
c = _compiler_from_config_entry(config["compiler"])
if c in known_compilers:
continue
new_compilers.append(c)
add_compilers_to_config(new_compilers, scope=scope)
return new_compilers
def select_new_compilers(compilers, scope=None):
@@ -462,7 +319,9 @@ def select_new_compilers(compilers, scope=None):
compilers_not_in_config = []
for c in compilers:
arch_spec = spack.spec.ArchSpec((None, c.operating_system, c.target))
same_specs = compilers_for_spec(c.spec, arch_spec, scope=scope, init_config=False)
same_specs = compilers_for_spec(
c.spec, arch_spec=arch_spec, scope=scope, init_config=False
)
if not same_specs:
compilers_not_in_config.append(c)
@@ -510,8 +369,9 @@ def replace_apple_clang(name):
return [replace_apple_clang(name) for name in all_compiler_module_names()]
@llnl.util.lang.memoized
def all_compiler_module_names() -> List[str]:
return [name for name in llnl.util.lang.list_modules(spack.paths.compilers_path)]
return list(llnl.util.lang.list_modules(spack.paths.compilers_path))
@_auto_compiler_spec
@@ -531,7 +391,12 @@ def find(compiler_spec, scope=None, init_config=True):
def find_specs_by_arch(compiler_spec, arch_spec, scope=None, init_config=True):
"""Return specs of available compilers that match the supplied
compiler spec. Return an empty list if nothing found."""
return [c.spec for c in compilers_for_spec(compiler_spec, arch_spec, scope, True, init_config)]
return [
c.spec
for c in compilers_for_spec(
compiler_spec, arch_spec=arch_spec, scope=scope, init_config=init_config
)
]
def all_compilers(scope=None, init_config=True):
@@ -553,14 +418,11 @@ def all_compilers_from(configuration, scope=None, init_config=True):
@_auto_compiler_spec
def compilers_for_spec(
compiler_spec, arch_spec=None, scope=None, use_cache=True, init_config=True
):
def compilers_for_spec(compiler_spec, *, arch_spec=None, scope=None, init_config=True):
"""This gets all compilers that satisfy the supplied CompilerSpec.
Returns an empty list if none are found.
"""
config = all_compilers_config(spack.config.CONFIG, scope=scope, init_config=init_config)
matches = set(find(compiler_spec, scope, init_config))
compilers = []
for cspec in matches:
@@ -569,7 +431,7 @@ def compilers_for_spec(
def compilers_for_arch(arch_spec, scope=None):
config = all_compilers_config(spack.config.CONFIG, scope=scope)
config = all_compilers_config(spack.config.CONFIG, scope=scope, init_config=False)
return list(get_compilers(config, arch_spec=arch_spec))
@@ -601,13 +463,15 @@ def compiler_from_dict(items):
os = items.get("operating_system", None)
target = items.get("target", None)
if not ("paths" in items and all(n in items["paths"] for n in _path_instance_vars)):
if not (
"paths" in items and all(n in items["paths"] for n in spack.compiler.PATH_INSTANCE_VARS)
):
raise InvalidCompilerConfigurationError(cspec)
cls = class_for_compiler_name(cspec.name)
compiler_paths = []
for c in _path_instance_vars:
for c in spack.compiler.PATH_INSTANCE_VARS:
compiler_path = items["paths"][c]
if compiler_path != "None":
compiler_paths.append(compiler_path)
@@ -735,24 +599,6 @@ def compiler_for_spec(compiler_spec, arch_spec):
return compilers[0]
@_auto_compiler_spec
def get_compiler_duplicates(compiler_spec, arch_spec):
config = spack.config.CONFIG
scope_to_compilers = {}
for scope in config.scopes:
compilers = compilers_for_spec(compiler_spec, arch_spec=arch_spec, scope=scope)
if compilers:
scope_to_compilers[scope] = compilers
cfg_file_to_duplicates = {}
for scope, compilers in scope_to_compilers.items():
config_file = config.get_config_filename(scope, "compilers")
cfg_file_to_duplicates[config_file] = compilers
return cfg_file_to_duplicates
@llnl.util.lang.memoized
def class_for_compiler_name(compiler_name):
"""Given a compiler module name, get the corresponding Compiler class."""
@@ -766,7 +612,7 @@ def class_for_compiler_name(compiler_name):
submodule_name = compiler_name.replace("-", "_")
module_name = ".".join(["spack", "compilers", submodule_name])
module_obj = __import__(module_name, fromlist=[None])
module_obj = importlib.import_module(module_name)
cls = getattr(module_obj, mod_to_class(compiler_name))
# make a note of the name in the module so we can get to it easily.
@@ -775,272 +621,10 @@ def class_for_compiler_name(compiler_name):
return cls
def all_os_classes():
"""
Return the list of classes for all operating systems available on
this platform
"""
classes = []
platform = spack.platforms.host()
for os_class in platform.operating_sys.values():
classes.append(os_class)
return classes
def all_compiler_types():
return [class_for_compiler_name(c) for c in supported_compilers()]
#: Gathers the attribute values by which a detected compiler is considered
#: unique in Spack.
#:
#: - os: the operating system
#: - compiler_name: the name of the compiler (e.g. 'gcc', 'clang', etc.)
#: - version: the version of the compiler
#:
CompilerID = collections.namedtuple("CompilerID", ["os", "compiler_name", "version"])
#: Variations on a matched compiler name
NameVariation = collections.namedtuple("NameVariation", ["prefix", "suffix"])
#: Groups together the arguments needed by `detect_version`. The four entries
#: in the tuple are:
#:
#: - id: An instance of the CompilerID named tuple (version can be set to None
#: as it will be detected later)
#: - variation: a NameVariation for file being tested
#: - language: compiler language being tested (one of 'cc', 'cxx', 'fc', 'f77')
#: - path: full path to the executable being tested
#:
DetectVersionArgs = collections.namedtuple(
"DetectVersionArgs", ["id", "variation", "language", "path"]
)
def arguments_to_detect_version_fn(
operating_system: spack.operating_systems.OperatingSystem, paths: List[str]
) -> List[DetectVersionArgs]:
"""Returns a list of DetectVersionArgs tuples to be used in a
corresponding function to detect compiler versions.
The ``operating_system`` instance can customize the behavior of this
function by providing a method called with the same name.
Args:
operating_system: the operating system on which we are looking for compilers
paths: paths to search for compilers
Returns:
List of DetectVersionArgs tuples. Each item in the list will be later
mapped to the corresponding function call to detect the version of the
compilers in this OS.
"""
def _default(search_paths: List[str]) -> List[DetectVersionArgs]:
command_arguments: List[DetectVersionArgs] = []
files_to_be_tested = fs.files_in(*search_paths)
for compiler_name in supported_compilers_for_host_platform():
compiler_cls = class_for_compiler_name(compiler_name)
for language in ("cc", "cxx", "f77", "fc"):
# Select only the files matching a regexp
for (file, full_path), regexp in itertools.product(
files_to_be_tested, compiler_cls.search_regexps(language)
):
match = regexp.match(file)
if match:
compiler_id = CompilerID(operating_system, compiler_name, None)
detect_version_args = DetectVersionArgs(
id=compiler_id,
variation=NameVariation(*match.groups()),
language=language,
path=full_path,
)
command_arguments.append(detect_version_args)
return command_arguments
fn = getattr(operating_system, "arguments_to_detect_version_fn", _default)
return fn(paths)
def detect_version(
detect_version_args: DetectVersionArgs,
) -> Tuple[Optional[DetectVersionArgs], Optional[str]]:
"""Computes the version of a compiler and adds it to the information
passed as input.
As this function is meant to be executed by worker processes it won't
raise any exception but instead will return a (value, error) tuple that
needs to be checked by the code dispatching the calls.
Args:
detect_version_args: information on the compiler for which we should detect the version.
Returns:
A ``(DetectVersionArgs, error)`` tuple. If ``error`` is ``None`` the
version of the compiler was computed correctly and the first argument
of the tuple will contain it. Otherwise ``error`` is a string
containing an explanation on why the version couldn't be computed.
"""
def _default(fn_args):
compiler_id = fn_args.id
language = fn_args.language
compiler_cls = class_for_compiler_name(compiler_id.compiler_name)
path = fn_args.path
# Get compiler names and the callback to detect their versions
callback = getattr(compiler_cls, f"{language}_version")
try:
version = callback(path)
if version and str(version).strip() and version != "unknown":
value = fn_args._replace(id=compiler_id._replace(version=version))
return value, None
error = f"Couldn't get version for compiler {path}".format(path)
except spack.util.executable.ProcessError as e:
error = f"Couldn't get version for compiler {path}\n" + str(e)
except spack.util.executable.ProcessTimeoutError as e:
error = f"Couldn't get version for compiler {path}\n" + str(e)
except Exception as e:
# Catching "Exception" here is fine because it just
# means something went wrong running a candidate executable.
error = "Error while executing candidate compiler {0}" "\n{1}: {2}".format(
path, e.__class__.__name__, str(e)
)
return None, error
operating_system = detect_version_args.id.os
fn = getattr(operating_system, "detect_version", _default)
return fn(detect_version_args)
def make_compiler_list(
detected_versions: List[DetectVersionArgs], mixed_toolchain: bool = False
) -> List["spack.compiler.Compiler"]:
"""Process a list of detected versions and turn them into a list of
compiler specs.
Args:
detected_versions: list of DetectVersionArgs containing a valid version
mixed_toolchain: allow mixing compilers from different toolchains if langauge is missing
Returns:
list: list of Compiler objects
"""
group_fn = lambda x: (x.id, x.variation, x.language)
sorted_compilers = sorted(detected_versions, key=group_fn)
# Gather items in a dictionary by the id, name variation and language
compilers_d: Dict[CompilerID, Dict[NameVariation, dict]] = {}
for sort_key, group in itertools.groupby(sorted_compilers, key=group_fn):
compiler_id, name_variation, language = sort_key
by_compiler_id = compilers_d.setdefault(compiler_id, {})
by_name_variation = by_compiler_id.setdefault(name_variation, {})
by_name_variation[language] = next(x.path for x in group)
def _default_make_compilers(cmp_id, paths):
operating_system, compiler_name, version = cmp_id
compiler_cls = class_for_compiler_name(compiler_name)
spec = spack.spec.CompilerSpec(compiler_cls.name, f"={version}")
paths = [paths.get(x, None) for x in ("cc", "cxx", "f77", "fc")]
# TODO: johnwparent - revist the following line as per discussion at:
# https://github.com/spack/spack/pull/33385/files#r1040036318
target = archspec.cpu.host()
compiler = compiler_cls(spec, operating_system, str(target.family), paths)
return [compiler]
# For compilers with the same compiler id:
#
# - Prefer with C compiler to without
# - Prefer with C++ compiler to without
# - Prefer no variations to variations (e.g., clang to clang-gpu)
#
sort_fn = lambda variation: (
"cc" not in by_compiler_id[variation], # None last
"cxx" not in by_compiler_id[variation], # None last
getattr(variation, "prefix", None),
getattr(variation, "suffix", None),
)
# Flatten to a list of compiler id, primary variation and compiler dictionary
flat_compilers: List[Tuple[CompilerID, NameVariation, dict]] = []
for compiler_id, by_compiler_id in compilers_d.items():
ordered = sorted(by_compiler_id, key=sort_fn)
selected_variation = ordered[0]
selected = by_compiler_id[selected_variation]
# Fill any missing parts from subsequent entries (without mixing toolchains)
for lang in ["cxx", "f77", "fc"]:
if lang not in selected:
next_lang = next(
(by_compiler_id[v][lang] for v in ordered if lang in by_compiler_id[v]), None
)
if next_lang:
selected[lang] = next_lang
flat_compilers.append((compiler_id, selected_variation, selected))
# Next, fill out the blanks of missing compilers by creating a mixed toolchain (if requested)
if mixed_toolchain:
make_mixed_toolchain(flat_compilers)
# Finally, create the compiler list
compilers: List["spack.compiler.Compiler"] = []
for compiler_id, _, compiler in flat_compilers:
make_compilers = getattr(compiler_id.os, "make_compilers", _default_make_compilers)
candidates = make_compilers(compiler_id, compiler)
compilers.extend(x for x in candidates if x.cc is not None)
return compilers
def make_mixed_toolchain(compilers: List[Tuple[CompilerID, NameVariation, dict]]) -> None:
"""Add missing compilers across toolchains when they are missing for a particular language.
This currently only adds the most sensible gfortran to (apple)-clang if it doesn't have a
fortran compiler (no flang)."""
# First collect the clangs that are missing a fortran compiler
clangs_without_flang = [
(id, variation, compiler)
for id, variation, compiler in compilers
if id.compiler_name in ("clang", "apple-clang")
and "f77" not in compiler
and "fc" not in compiler
]
if not clangs_without_flang:
return
# Filter on GCCs with fortran compiler
gccs_with_fortran = [
(id, variation, compiler)
for id, variation, compiler in compilers
if id.compiler_name == "gcc" and "f77" in compiler and "fc" in compiler
]
# Sort these GCCs by "best variation" (no prefix / suffix first)
gccs_with_fortran.sort(
key=lambda x: (getattr(x[1], "prefix", None), getattr(x[1], "suffix", None))
)
# Attach the optimal GCC fortran compiler to the clangs that don't have one
for clang_id, _, clang_compiler in clangs_without_flang:
gcc_compiler = next(
(gcc[2] for gcc in gccs_with_fortran if gcc[0].os == clang_id.os), None
)
if not gcc_compiler:
continue
# Update the fc / f77 entries
clang_compiler["f77"] = gcc_compiler["f77"]
clang_compiler["fc"] = gcc_compiler["fc"]
def is_mixed_toolchain(compiler):
"""Returns True if the current compiler is a mixed toolchain,
False otherwise.
@@ -1087,20 +671,164 @@ def name_matches(name, name_list):
return False
_EXTRA_ATTRIBUTES_KEY = "extra_attributes"
_COMPILERS_KEY = "compilers"
_C_KEY = "c"
_CXX_KEY, _FORTRAN_KEY = "cxx", "fortran"
class CompilerConfigFactory:
"""Class aggregating all ways of constructing a list of compiler config entries."""
@staticmethod
def from_specs(specs: List["spack.spec.Spec"]) -> List[dict]:
result = []
compiler_package_names = supported_compilers() + list(package_name_to_compiler_name.keys())
for s in specs:
if s.name not in compiler_package_names:
continue
candidate = CompilerConfigFactory.from_external_spec(s)
if candidate is None:
continue
result.append(candidate)
return result
@staticmethod
def from_packages_yaml(packages_yaml) -> List[dict]:
compiler_specs = []
compiler_package_names = supported_compilers() + list(package_name_to_compiler_name.keys())
for name, entry in packages_yaml.items():
if name not in compiler_package_names:
continue
externals_config = entry.get("externals", None)
if not externals_config:
continue
current_specs = []
for current_external in externals_config:
compiler = CompilerConfigFactory._spec_from_external_config(current_external)
if compiler:
current_specs.append(compiler)
compiler_specs.extend(current_specs)
return CompilerConfigFactory.from_specs(compiler_specs)
@staticmethod
def _spec_from_external_config(config):
# Allow `@x.y.z` instead of `@=x.y.z`
err_header = f"The external spec '{config['spec']}' cannot be used as a compiler"
# If extra_attributes is not there I might not want to use this entry as a compiler,
# therefore just leave a debug message, but don't be loud with a warning.
if _EXTRA_ATTRIBUTES_KEY not in config:
tty.debug(f"[{__file__}] {err_header}: missing the '{_EXTRA_ATTRIBUTES_KEY}' key")
return None
extra_attributes = config[_EXTRA_ATTRIBUTES_KEY]
result = spack.spec.Spec(
str(spack.spec.parse_with_version_concrete(config["spec"])),
external_modules=config.get("modules"),
)
result.extra_attributes = extra_attributes
return result
@staticmethod
def from_external_spec(spec: "spack.spec.Spec") -> Optional[dict]:
spec = spack.spec.parse_with_version_concrete(spec)
extra_attributes = getattr(spec, _EXTRA_ATTRIBUTES_KEY, None)
if extra_attributes is None:
return None
paths = CompilerConfigFactory._extract_compiler_paths(spec)
if paths is None:
return None
compiler_spec = spack.spec.CompilerSpec(
package_name_to_compiler_name.get(spec.name, spec.name), spec.version
)
operating_system, target = CompilerConfigFactory._extract_os_and_target(spec)
compiler_entry = {
"compiler": {
"spec": str(compiler_spec),
"paths": paths,
"flags": extra_attributes.get("flags", {}),
"operating_system": str(operating_system),
"target": str(target.family),
"modules": getattr(spec, "external_modules", []),
"environment": extra_attributes.get("environment", {}),
"extra_rpaths": extra_attributes.get("extra_rpaths", []),
"implicit_rpaths": extra_attributes.get("implicit_rpaths", None),
}
}
return compiler_entry
@staticmethod
def _extract_compiler_paths(spec: "spack.spec.Spec") -> Optional[Dict[str, str]]:
err_header = f"The external spec '{spec}' cannot be used as a compiler"
extra_attributes = spec.extra_attributes
# If I have 'extra_attributes' warn if 'compilers' is missing,
# or we don't have a C compiler
if _COMPILERS_KEY not in extra_attributes:
warnings.warn(
f"{err_header}: missing the '{_COMPILERS_KEY}' key under '{_EXTRA_ATTRIBUTES_KEY}'"
)
return None
attribute_compilers = extra_attributes[_COMPILERS_KEY]
if _C_KEY not in attribute_compilers:
warnings.warn(
f"{err_header}: missing the C compiler path under "
f"'{_EXTRA_ATTRIBUTES_KEY}:{_COMPILERS_KEY}'"
)
return None
c_compiler = attribute_compilers[_C_KEY]
# C++ and Fortran compilers are not mandatory, so let's just leave a debug trace
if _CXX_KEY not in attribute_compilers:
tty.debug(f"[{__file__}] The external spec {spec} does not have a C++ compiler")
if _FORTRAN_KEY not in attribute_compilers:
tty.debug(f"[{__file__}] The external spec {spec} does not have a Fortran compiler")
# compilers format has cc/fc/f77, externals format has "c/fortran"
return {
"cc": c_compiler,
"cxx": attribute_compilers.get(_CXX_KEY, None),
"fc": attribute_compilers.get(_FORTRAN_KEY, None),
"f77": attribute_compilers.get(_FORTRAN_KEY, None),
}
@staticmethod
def _extract_os_and_target(spec: "spack.spec.Spec"):
if not spec.architecture:
host_platform = spack.platforms.host()
operating_system = host_platform.operating_system("default_os")
target = host_platform.target("default_target").microarchitecture
else:
target = spec.architecture.target
if not target:
target = spack.platforms.host().target("default_target")
target = target.microarchitecture
operating_system = spec.os
if not operating_system:
host_platform = spack.platforms.host()
operating_system = host_platform.operating_system("default_os")
return operating_system, target
class InvalidCompilerConfigurationError(spack.error.SpackError):
def __init__(self, compiler_spec):
super().__init__(
'Invalid configuration for [compiler "%s"]: ' % compiler_spec,
"Compiler configuration must contain entries for all compilers: %s"
% _path_instance_vars,
f'Invalid configuration for [compiler "{compiler_spec}"]: ',
f"Compiler configuration must contain entries for "
f"all compilers: {spack.compiler.PATH_INSTANCE_VARS}",
)
class NoCompilersError(spack.error.SpackError):
def __init__(self):
super().__init__("Spack could not find any compilers!")
class UnknownCompilerError(spack.error.SpackError):
def __init__(self, compiler_name):
super().__init__("Spack doesn't support the requested compiler: {0}".format(compiler_name))
@@ -1111,25 +839,3 @@ def __init__(self, compiler_spec, target):
super().__init__(
"No compilers for operating system %s satisfy spec %s" % (target, compiler_spec)
)
class CompilerDuplicateError(spack.error.SpackError):
def __init__(self, compiler_spec, arch_spec):
config_file_to_duplicates = get_compiler_duplicates(compiler_spec, arch_spec)
duplicate_table = list((x, len(y)) for x, y in config_file_to_duplicates.items())
descriptor = lambda num: "time" if num == 1 else "times"
duplicate_msg = lambda cfgfile, count: "{0}: {1} {2}".format(
cfgfile, str(count), descriptor(count)
)
msg = (
"Compiler configuration contains entries with duplicate"
+ " specification ({0}, {1})".format(compiler_spec, arch_spec)
+ " in the following files:\n\t"
+ "\n\t".join(duplicate_msg(x, y) for x, y in duplicate_table)
)
super().__init__(msg)
class CompilerSpecInsufficientlySpecificError(spack.error.SpackError):
def __init__(self, compiler_spec):
super().__init__("Multiple compilers satisfy spec %s" % compiler_spec)

View File

@@ -223,6 +223,30 @@ def get_oneapi_root(pth: str):
)
self.msvc_compiler_environment = CmdCall(*env_cmds)
@property
def cxx11_flag(self):
return "/std:c++11"
@property
def cxx14_flag(self):
return "/std:c++14"
@property
def cxx17_flag(self):
return "/std:c++17"
@property
def cxx20_flag(self):
return "/std:c++20"
@property
def c11_flag(self):
return "/std:c11"
@property
def c17_flag(self):
return "/std:c17"
@property
def msvc_version(self):
"""This is the VCToolset version *NOT* the actual version of the cl compiler

View File

@@ -2,31 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""
Functions here are used to take abstract specs and make them concrete.
For example, if a spec asks for a version between 1.8 and 1.9, these
functions might take will take the most recent 1.9 version of the
package available. Or, if the user didn't specify a compiler for a
spec, then this will assign a compiler to the spec based on defaults
or user preferences.
TODO: make this customizable and allow users to configure
concretization policies.
(DEPRECATED) Used to contain the code for the original concretizer
"""
import functools
import platform
import tempfile
from contextlib import contextmanager
from itertools import chain
from typing import Union
import archspec.cpu
import llnl.util.lang
import llnl.util.tty as tty
import spack.abi
import spack.compilers
import spack.config
import spack.environment
@@ -37,655 +18,24 @@
import spack.target
import spack.tengine
import spack.util.path
import spack.variant as vt
from spack.package_prefs import PackagePrefs, is_spec_buildable, spec_externals
from spack.version import ClosedOpenRange, VersionList, ver
#: impements rudimentary logic for ABI compatibility
_abi: Union[spack.abi.ABI, llnl.util.lang.Singleton] = llnl.util.lang.Singleton(
lambda: spack.abi.ABI()
)
@functools.total_ordering
class reverse_order:
"""Helper for creating key functions.
This is a wrapper that inverts the sense of the natural
comparisons on the object.
"""
def __init__(self, value):
self.value = value
def __eq__(self, other):
return other.value == self.value
def __lt__(self, other):
return other.value < self.value
class Concretizer:
"""You can subclass this class to override some of the default
concretization strategies, or you can override all of them.
"""
#: Controls whether we check that compiler versions actually exist
#: during concretization. Used for testing and for mirror creation
check_for_compiler_existence = None
#: Packages that the old concretizer cannot deal with correctly, and cannot build anyway.
#: Those will not be considered as providers for virtuals.
non_buildable_packages = {"glibc", "musl"}
def __init__(self, abstract_spec=None):
if Concretizer.check_for_compiler_existence is None:
Concretizer.check_for_compiler_existence = not spack.config.get(
"config:install_missing_compilers", False
)
self.abstract_spec = abstract_spec
self._adjust_target_answer_generator = None
def concretize_develop(self, spec):
"""
Add ``dev_path=*`` variant to packages built from local source.
"""
env = spack.environment.active_environment()
dev_info = env.dev_specs.get(spec.name, {}) if env else {}
if not dev_info:
return False
path = spack.util.path.canonicalize_path(dev_info["path"], default_wd=env.path)
if "dev_path" in spec.variants:
assert spec.variants["dev_path"].value == path
changed = False
else:
spec.variants.setdefault("dev_path", vt.SingleValuedVariant("dev_path", path))
changed = True
changed |= spec.constrain(dev_info["spec"])
return changed
def _valid_virtuals_and_externals(self, spec):
"""Returns a list of candidate virtual dep providers and external
packages that coiuld be used to concretize a spec.
Preferred specs come first in the list.
"""
# First construct a list of concrete candidates to replace spec with.
candidates = [spec]
pref_key = lambda spec: 0 # no-op pref key
if spec.virtual:
candidates = [
s
for s in spack.repo.PATH.providers_for(spec)
if s.name not in self.non_buildable_packages
]
if not candidates:
raise spack.error.UnsatisfiableProviderSpecError(candidates[0], spec)
# Find nearest spec in the DAG (up then down) that has prefs.
spec_w_prefs = find_spec(
spec, lambda p: PackagePrefs.has_preferred_providers(p.name, spec.name), spec
) # default to spec itself.
# Create a key to sort candidates by the prefs we found
pref_key = PackagePrefs(spec_w_prefs.name, "providers", spec.name)
# For each candidate package, if it has externals, add those
# to the usable list. if it's not buildable, then *only* add
# the externals.
usable = []
for cspec in candidates:
if is_spec_buildable(cspec):
usable.append(cspec)
externals = spec_externals(cspec)
for ext in externals:
if ext.intersects(spec):
usable.append(ext)
# If nothing is in the usable list now, it's because we aren't
# allowed to build anything.
if not usable:
raise NoBuildError(spec)
# Use a sort key to order the results
return sorted(
usable,
key=lambda spec: (
not spec.external, # prefer externals
pref_key(spec), # respect prefs
spec.name, # group by name
reverse_order(spec.versions), # latest version
spec, # natural order
),
)
def choose_virtual_or_external(self, spec: spack.spec.Spec):
"""Given a list of candidate virtual and external packages, try to
find one that is most ABI compatible.
"""
candidates = self._valid_virtuals_and_externals(spec)
if not candidates:
return candidates
# Find the nearest spec in the dag that has a compiler. We'll
# use that spec to calibrate compiler compatibility.
abi_exemplar = find_spec(spec, lambda x: x.compiler)
if abi_exemplar is None:
abi_exemplar = spec.root
# Sort candidates from most to least compatibility.
# We reverse because True > False.
# Sort is stable, so candidates keep their order.
return sorted(
candidates,
reverse=True,
key=lambda spec: (
_abi.compatible(spec, abi_exemplar, loose=True),
_abi.compatible(spec, abi_exemplar),
),
)
def concretize_version(self, spec):
"""If the spec is already concrete, return. Otherwise take
the preferred version from spackconfig, and default to the package's
version if there are no available versions.
TODO: In many cases we probably want to look for installed
versions of each package and use an installed version
if we can link to it. The policy implemented here will
tend to rebuild a lot of stuff becasue it will prefer
a compiler in the spec to any compiler already-
installed things were built with. There is likely
some better policy that finds some middle ground
between these two extremes.
"""
# return if already concrete.
if spec.versions.concrete:
return False
# List of versions we could consider, in sorted order
pkg_versions = spec.package_class.versions
usable = [v for v in pkg_versions if any(v.intersects(sv) for sv in spec.versions)]
yaml_prefs = PackagePrefs(spec.name, "version")
# The keys below show the order of precedence of factors used
# to select a version when concretizing. The item with
# the "largest" key will be selected.
#
# NOTE: When COMPARING VERSIONS, the '@develop' version is always
# larger than other versions. BUT when CONCRETIZING,
# the largest NON-develop version is selected by default.
keyfn = lambda v: (
# ------- Special direction from the user
# Respect order listed in packages.yaml
-yaml_prefs(v),
# The preferred=True flag (packages or packages.yaml or both?)
pkg_versions.get(v).get("preferred", False),
# ------- Regular case: use latest non-develop version by default.
# Avoid @develop version, which would otherwise be the "largest"
# in straight version comparisons
not v.isdevelop(),
# Compare the version itself
# This includes the logic:
# a) develop > everything (disabled by "not v.isdevelop() above)
# b) numeric > non-numeric
# c) Numeric or string comparison
v,
)
usable.sort(key=keyfn, reverse=True)
if usable:
spec.versions = ver([usable[0]])
else:
# We don't know of any SAFE versions that match the given
# spec. Grab the spec's versions and grab the highest
# *non-open* part of the range of versions it specifies.
# Someone else can raise an error if this happens,
# e.g. when we go to fetch it and don't know how. But it
# *might* work.
if not spec.versions or spec.versions == VersionList([":"]):
raise NoValidVersionError(spec)
else:
last = spec.versions[-1]
if isinstance(last, ClosedOpenRange):
range_as_version = VersionList([last]).concrete_range_as_version
if range_as_version:
spec.versions = ver([range_as_version])
else:
raise NoValidVersionError(spec)
else:
spec.versions = ver([last])
return True # Things changed
def concretize_architecture(self, spec):
"""If the spec is empty provide the defaults of the platform. If the
architecture is not a string type, then check if either the platform,
target or operating system are concretized. If any of the fields are
changed then return True. If everything is concretized (i.e the
architecture attribute is a namedtuple of classes) then return False.
If the target is a string type, then convert the string into a
concretized architecture. If it has no architecture and the root of the
DAG has an architecture, then use the root otherwise use the defaults
on the platform.
"""
# ensure type safety for the architecture
if spec.architecture is None:
spec.architecture = spack.spec.ArchSpec()
if spec.architecture.concrete:
return False
# Get platform of nearest spec with a platform, including spec
# If spec has a platform, easy
if spec.architecture.platform:
new_plat = spack.platforms.by_name(spec.architecture.platform)
else:
# Else if anyone else has a platform, take the closest one
# Search up, then down, along build/link deps first
# Then any nearest. Algorithm from compilerspec search
platform_spec = find_spec(spec, lambda x: x.architecture and x.architecture.platform)
if platform_spec:
new_plat = spack.platforms.by_name(platform_spec.architecture.platform)
else:
# If no platform anywhere in this spec, grab the default
new_plat = spack.platforms.host()
# Get nearest spec with relevant platform and an os
# Generally, same algorithm as finding platform, except we only
# consider specs that have a platform
if spec.architecture.os:
new_os = spec.architecture.os
else:
new_os_spec = find_spec(
spec,
lambda x: (
x.architecture
and x.architecture.platform == str(new_plat)
and x.architecture.os
),
)
if new_os_spec:
new_os = new_os_spec.architecture.os
else:
new_os = new_plat.operating_system("default_os")
# Get the nearest spec with relevant platform and a target
# Generally, same algorithm as finding os
curr_target = None
if spec.architecture.target:
curr_target = spec.architecture.target
if spec.architecture.target and spec.architecture.target_concrete:
new_target = spec.architecture.target
else:
new_target_spec = find_spec(
spec,
lambda x: (
x.architecture
and x.architecture.platform == str(new_plat)
and x.architecture.target
and x.architecture.target != curr_target
),
)
if new_target_spec:
if curr_target:
# constrain one target by the other
new_target_arch = spack.spec.ArchSpec(
(None, None, new_target_spec.architecture.target)
)
curr_target_arch = spack.spec.ArchSpec((None, None, curr_target))
curr_target_arch.constrain(new_target_arch)
new_target = curr_target_arch.target
else:
new_target = new_target_spec.architecture.target
else:
# To get default platform, consider package prefs
if PackagePrefs.has_preferred_targets(spec.name):
new_target = self.target_from_package_preferences(spec)
else:
new_target = new_plat.target("default_target")
if curr_target:
# convert to ArchSpec to compare satisfaction
new_target_arch = spack.spec.ArchSpec((None, None, str(new_target)))
curr_target_arch = spack.spec.ArchSpec((None, None, str(curr_target)))
if not new_target_arch.intersects(curr_target_arch):
# new_target is an incorrect guess based on preferences
# and/or default
valid_target_ranges = str(curr_target).split(",")
for target_range in valid_target_ranges:
t_min, t_sep, t_max = target_range.partition(":")
if not t_sep:
new_target = t_min
break
elif t_max:
new_target = t_max
break
elif t_min:
# TODO: something better than picking first
new_target = t_min
break
# Construct new architecture, compute whether spec changed
arch_spec = (str(new_plat), str(new_os), str(new_target))
new_arch = spack.spec.ArchSpec(arch_spec)
spec_changed = new_arch != spec.architecture
spec.architecture = new_arch
return spec_changed
def target_from_package_preferences(self, spec):
"""Returns the preferred target from the package preferences if
there's any.
Args:
spec: abstract spec to be concretized
"""
target_prefs = PackagePrefs(spec.name, "target")
target_specs = [spack.spec.Spec("target=%s" % tname) for tname in archspec.cpu.TARGETS]
def tspec_filter(s):
# Filter target specs by whether the architecture
# family is the current machine type. This ensures
# we only consider x86_64 targets when on an
# x86_64 machine, etc. This may need to change to
# enable setting cross compiling as a default
target = archspec.cpu.TARGETS[str(s.architecture.target)]
arch_family_name = target.family.name
return arch_family_name == platform.machine()
# Sort filtered targets by package prefs
target_specs = list(filter(tspec_filter, target_specs))
target_specs.sort(key=target_prefs)
new_target = target_specs[0].architecture.target
return new_target
def concretize_variants(self, spec):
"""If the spec already has variants filled in, return. Otherwise, add
the user preferences from packages.yaml or the default variants from
the package specification.
"""
changed = False
preferred_variants = PackagePrefs.preferred_variants(spec.name)
pkg_cls = spec.package_class
for name, entry in pkg_cls.variants.items():
variant, when = entry
var = spec.variants.get(name, None)
if var and "*" in var:
# remove variant wildcard before concretizing
# wildcard cannot be combined with other variables in a
# multivalue variant, a concrete variant cannot have the value
# wildcard, and a wildcard does not constrain a variant
spec.variants.pop(name)
if name not in spec.variants and any(spec.satisfies(w) for w in when):
changed = True
if name in preferred_variants:
spec.variants[name] = preferred_variants.get(name)
else:
spec.variants[name] = variant.make_default()
if name in spec.variants and not any(spec.satisfies(w) for w in when):
raise vt.InvalidVariantForSpecError(name, when, spec)
return changed
def concretize_compiler(self, spec):
"""If the spec already has a compiler, we're done. If not, then take
the compiler used for the nearest ancestor with a compiler
spec and use that. If the ancestor's compiler is not
concrete, then used the preferred compiler as specified in
spackconfig.
Intuition: Use the spackconfig default if no package that depends on
this one has a strict compiler requirement. Otherwise, try to
build with the compiler that will be used by libraries that
link to this one, to maximize compatibility.
"""
# Pass on concretizing the compiler if the target or operating system
# is not yet determined
if not spec.architecture.concrete:
# We haven't changed, but other changes need to happen before we
# continue. `return True` here to force concretization to keep
# running.
return True
# Only use a matching compiler if it is of the proper style
# Takes advantage of the proper logic already existing in
# compiler_for_spec Should think whether this can be more
# efficient
def _proper_compiler_style(cspec, aspec):
compilers = spack.compilers.compilers_for_spec(cspec, arch_spec=aspec)
# If the spec passed as argument is concrete we want to check
# the versions match exactly
if (
cspec.concrete
and compilers
and cspec.version not in [c.version for c in compilers]
):
return []
return compilers
if spec.compiler and spec.compiler.concrete:
if self.check_for_compiler_existence and not _proper_compiler_style(
spec.compiler, spec.architecture
):
_compiler_concretization_failure(spec.compiler, spec.architecture)
return False
# Find another spec that has a compiler, or the root if none do
other_spec = spec if spec.compiler else find_spec(spec, lambda x: x.compiler, spec.root)
other_compiler = other_spec.compiler
assert other_spec
# Check if the compiler is already fully specified
if other_compiler and other_compiler.concrete:
if self.check_for_compiler_existence and not _proper_compiler_style(
other_compiler, spec.architecture
):
_compiler_concretization_failure(other_compiler, spec.architecture)
spec.compiler = other_compiler
return True
if other_compiler: # Another node has abstract compiler information
compiler_list = spack.compilers.find_specs_by_arch(other_compiler, spec.architecture)
if not compiler_list:
# We don't have a matching compiler installed
if not self.check_for_compiler_existence:
# Concretize compiler spec versions as a package to build
cpkg_spec = spack.compilers.pkg_spec_for_compiler(other_compiler)
self.concretize_version(cpkg_spec)
spec.compiler = spack.spec.CompilerSpec(
other_compiler.name, cpkg_spec.versions
)
return True
else:
# No compiler with a satisfactory spec was found
raise UnavailableCompilerVersionError(other_compiler, spec.architecture)
else:
# We have no hints to go by, grab any compiler
compiler_list = spack.compilers.all_compiler_specs()
if not compiler_list:
# Spack has no compilers.
raise spack.compilers.NoCompilersError()
# By default, prefer later versions of compilers
compiler_list = sorted(compiler_list, key=lambda x: (x.name, x.version), reverse=True)
ppk = PackagePrefs(other_spec.name, "compiler")
matches = sorted(compiler_list, key=ppk)
# copy concrete version into other_compiler
try:
spec.compiler = next(
c for c in matches if _proper_compiler_style(c, spec.architecture)
).copy()
except StopIteration:
# No compiler with a satisfactory spec has a suitable arch
_compiler_concretization_failure(other_compiler, spec.architecture)
assert spec.compiler.concrete
return True # things changed.
def concretize_compiler_flags(self, spec):
"""
The compiler flags are updated to match those of the spec whose
compiler is used, defaulting to no compiler flags in the spec.
Default specs set at the compiler level will still be added later.
"""
# Pass on concretizing the compiler flags if the target or operating
# system is not set.
if not spec.architecture.concrete:
# We haven't changed, but other changes need to happen before we
# continue. `return True` here to force concretization to keep
# running.
return True
compiler_match = lambda other: (
spec.compiler == other.compiler and spec.architecture == other.architecture
)
ret = False
for flag in spack.spec.FlagMap.valid_compiler_flags():
if flag not in spec.compiler_flags:
spec.compiler_flags[flag] = list()
try:
nearest = next(
p
for p in spec.traverse(direction="parents")
if (compiler_match(p) and (p is not spec) and flag in p.compiler_flags)
)
nearest_flags = nearest.compiler_flags.get(flag, [])
flags = spec.compiler_flags.get(flag, [])
if set(nearest_flags) - set(flags):
spec.compiler_flags[flag] = list(llnl.util.lang.dedupe(nearest_flags + flags))
ret = True
except StopIteration:
pass
# Include the compiler flag defaults from the config files
# This ensures that spack will detect conflicts that stem from a change
# in default compiler flags.
try:
compiler = spack.compilers.compiler_for_spec(spec.compiler, spec.architecture)
except spack.compilers.NoCompilerForSpecError:
if self.check_for_compiler_existence:
raise
return ret
for flag in compiler.flags:
config_flags = compiler.flags.get(flag, [])
flags = spec.compiler_flags.get(flag, [])
spec.compiler_flags[flag] = list(llnl.util.lang.dedupe(config_flags + flags))
if set(config_flags) - set(flags):
ret = True
return ret
def adjust_target(self, spec):
"""Adjusts the target microarchitecture if the compiler is too old
to support the default one.
Args:
spec: spec to be concretized
Returns:
True if spec was modified, False otherwise
"""
# To minimize the impact on performance this function will attempt
# to adjust the target only at the very first call once necessary
# information is set. It will just return False on subsequent calls.
# The way this is achieved is by initializing a generator and making
# this function return the next answer.
if not (spec.architecture and spec.architecture.concrete):
# Not ready, but keep going because we have work to do later
return True
def _make_only_one_call(spec):
yield self._adjust_target(spec)
while True:
yield False
if self._adjust_target_answer_generator is None:
self._adjust_target_answer_generator = _make_only_one_call(spec)
return next(self._adjust_target_answer_generator)
def _adjust_target(self, spec):
"""Assumes that the architecture and the compiler have been
set already and checks if the current target microarchitecture
is the default and can be optimized by the compiler.
If not, downgrades the microarchitecture until a suitable one
is found. If none can be found raise an error.
Args:
spec: spec to be concretized
Returns:
True if any modification happened, False otherwise
"""
import archspec.cpu
# Try to adjust the target only if it is the default
# target for this platform
current_target = spec.architecture.target
current_platform = spack.platforms.by_name(spec.architecture.platform)
default_target = current_platform.target("default_target")
if PackagePrefs.has_preferred_targets(spec.name):
default_target = self.target_from_package_preferences(spec)
if current_target != default_target or (
self.abstract_spec
and self.abstract_spec.architecture
and self.abstract_spec.architecture.concrete
):
return False
try:
current_target.optimization_flags(spec.compiler)
except archspec.cpu.UnsupportedMicroarchitecture:
microarchitecture = current_target.microarchitecture
for ancestor in microarchitecture.ancestors:
candidate = None
try:
candidate = spack.target.Target(ancestor)
candidate.optimization_flags(spec.compiler)
except archspec.cpu.UnsupportedMicroarchitecture:
continue
if candidate is not None:
msg = (
"{0.name}@{0.version} cannot build optimized "
'binaries for "{1}". Using best target possible: '
'"{2}"'
)
msg = msg.format(spec.compiler, current_target, candidate)
tty.warn(msg)
spec.architecture.target = candidate
return True
else:
raise
return False
CHECK_COMPILER_EXISTENCE = True
@contextmanager
def disable_compiler_existence_check():
saved = Concretizer.check_for_compiler_existence
Concretizer.check_for_compiler_existence = False
global CHECK_COMPILER_EXISTENCE
CHECK_COMPILER_EXISTENCE, saved = False, CHECK_COMPILER_EXISTENCE
yield
Concretizer.check_for_compiler_existence = saved
CHECK_COMPILER_EXISTENCE = saved
@contextmanager
def enable_compiler_existence_check():
saved = Concretizer.check_for_compiler_existence
Concretizer.check_for_compiler_existence = True
global CHECK_COMPILER_EXISTENCE
CHECK_COMPILER_EXISTENCE, saved = True, CHECK_COMPILER_EXISTENCE
yield
Concretizer.check_for_compiler_existence = saved
CHECK_COMPILER_EXISTENCE = saved
def find_spec(spec, condition, default=None):
@@ -719,19 +69,6 @@ def find_spec(spec, condition, default=None):
return default # Nothing matched the condition; return default.
def _compiler_concretization_failure(compiler_spec, arch):
# Distinguish between the case that there are compilers for
# the arch but not with the given compiler spec and the case that
# there are no compilers for the arch at all
if not spack.compilers.compilers_for_arch(arch):
available_os_targets = set(
(c.operating_system, c.target) for c in spack.compilers.all_compilers()
)
raise NoCompilersForArchError(arch, available_os_targets)
else:
raise UnavailableCompilerVersionError(compiler_spec, arch)
def concretize_specs_together(*abstract_specs, **kwargs):
"""Given a number of specs as input, tries to concretize them together.
@@ -744,12 +81,6 @@ def concretize_specs_together(*abstract_specs, **kwargs):
Returns:
List of concretized specs
"""
if spack.config.get("config:concretizer", "clingo") == "original":
return _concretize_specs_together_original(*abstract_specs, **kwargs)
return _concretize_specs_together_new(*abstract_specs, **kwargs)
def _concretize_specs_together_new(*abstract_specs, **kwargs):
import spack.solver.asp
allow_deprecated = spack.config.get("config:deprecated", False)
@@ -760,51 +91,6 @@ def _concretize_specs_together_new(*abstract_specs, **kwargs):
return [s.copy() for s in result.specs]
def _concretize_specs_together_original(*abstract_specs, **kwargs):
abstract_specs = [spack.spec.Spec(s) for s in abstract_specs]
tmpdir = tempfile.mkdtemp()
builder = spack.repo.MockRepositoryBuilder(tmpdir)
# Split recursive specs, as it seems the concretizer has issue
# respecting conditions on dependents expressed like
# depends_on('foo ^bar@1.0'), see issue #11160
split_specs = [
dep.copy(deps=False) for spec1 in abstract_specs for dep in spec1.traverse(root=True)
]
builder.add_package(
"concretizationroot", dependencies=[(str(x), None, None) for x in split_specs]
)
with spack.repo.use_repositories(builder.root, override=False):
# Spec from a helper package that depends on all the abstract_specs
concretization_root = spack.spec.Spec("concretizationroot")
concretization_root.concretize(tests=kwargs.get("tests", False))
# Retrieve the direct dependencies
concrete_specs = [concretization_root[spec.name].copy() for spec in abstract_specs]
return concrete_specs
class NoCompilersForArchError(spack.error.SpackError):
def __init__(self, arch, available_os_targets):
err_msg = (
"No compilers found"
" for operating system %s and target %s."
"\nIf previous installations have succeeded, the"
" operating system may have been updated." % (arch.os, arch.target)
)
available_os_target_strs = list()
for operating_system, t in available_os_targets:
os_target_str = "%s-%s" % (operating_system, t) if t else operating_system
available_os_target_strs.append(os_target_str)
err_msg += (
"\nCompilers are defined for the following"
" operating systems and targets:\n\t" + "\n\t".join(available_os_target_strs)
)
super().__init__(err_msg, "Run 'spack compiler find' to add compilers.")
class UnavailableCompilerVersionError(spack.error.SpackError):
"""Raised when there is no available compiler that satisfies a
compiler spec."""
@@ -820,37 +106,3 @@ def __init__(self, compiler_spec, arch=None):
"'spack compilers' to see which compilers are already recognized"
" by spack.",
)
class NoValidVersionError(spack.error.SpackError):
"""Raised when there is no way to have a concrete version for a
particular spec."""
def __init__(self, spec):
super().__init__(
"There are no valid versions for %s that match '%s'" % (spec.name, spec.versions)
)
class InsufficientArchitectureInfoError(spack.error.SpackError):
"""Raised when details on architecture cannot be collected from the
system"""
def __init__(self, spec, archs):
super().__init__(
"Cannot determine necessary architecture information for '%s': %s"
% (spec.name, str(archs))
)
class NoBuildError(spack.error.SpecError):
"""Raised when a package is configured with the buildable option False, but
no satisfactory external versions can be found
"""
def __init__(self, spec):
msg = (
"The spec\n '%s'\n is configured as not buildable, "
"and no matching external installs were found"
)
super().__init__(msg % spec)

View File

@@ -99,7 +99,6 @@
"dirty": False,
"build_jobs": min(16, cpus_available()),
"build_stage": "$tempdir/spack-stage",
"concretizer": "clingo",
"license_dir": spack.paths.default_license_dir,
}
}
@@ -1091,7 +1090,7 @@ def validate(
def read_config_file(
filename: str, schema: Optional[YamlConfigDict] = None
path: str, schema: Optional[YamlConfigDict] = None
) -> Optional[YamlConfigDict]:
"""Read a YAML configuration file.
@@ -1101,21 +1100,9 @@ def read_config_file(
# to preserve flexibility in calling convention (don't need to provide
# schema when it's not necessary) while allowing us to validate against a
# known schema when the top-level key could be incorrect.
if not os.path.exists(filename):
# Ignore nonexistent files.
tty.debug(f"Skipping nonexistent config path {filename}", level=3)
return None
elif not os.path.isfile(filename):
raise ConfigFileError(f"Invalid configuration. {filename} exists but is not a file.")
elif not os.access(filename, os.R_OK):
raise ConfigFileError(f"Config file is not readable: {filename}")
try:
tty.debug(f"Reading config from file {filename}")
with open(filename) as f:
with open(path) as f:
tty.debug(f"Reading config from file {path}")
data = syaml.load_config(f)
if data:
@@ -1126,15 +1113,20 @@ def read_config_file(
return data
except StopIteration:
raise ConfigFileError(f"Config file is empty or is not a valid YAML dict: {filename}")
except FileNotFoundError:
# Ignore nonexistent files.
tty.debug(f"Skipping nonexistent config path {path}", level=3)
return None
except OSError as e:
raise ConfigFileError(f"Path is not a file or is not readable: {path}: {str(e)}") from e
except StopIteration as e:
raise ConfigFileError(f"Config file is empty or is not a valid YAML dict: {path}") from e
except syaml.SpackYAMLError as e:
raise ConfigFileError(str(e)) from e
except OSError as e:
raise ConfigFileError(f"Error reading configuration file {filename}: {str(e)}") from e
def _override(string: str) -> bool:
"""Test if a spack YAML string is an override.

View File

@@ -308,8 +308,7 @@ def __call__(self):
return t.render(**self.to_dict())
import spack.container.writers.docker # noqa: E402
# Import after function definition all the modules in this package,
# so that registration of writers will happen automatically
import spack.container.writers.singularity # noqa: E402
from . import docker # noqa: F401 E402
from . import singularity # noqa: F401 E402

View File

@@ -14,12 +14,14 @@
import llnl.util.tty as tty
import spack.cmd
import spack.compilers
import spack.deptypes as dt
import spack.error
import spack.hash_types as hash_types
import spack.platforms
import spack.repo
import spack.spec
import spack.store
from spack.schema.cray_manifest import schema as manifest_schema
#: Cray systems can store a Spack-compatible description of system
@@ -237,7 +239,7 @@ def read(path, apply_updates):
tty.debug(f"Include this\n{traceback.format_exc()}")
if apply_updates:
for spec in specs.values():
spack.store.STORE.db.add(spec, directory_layout=None)
spack.store.STORE.db.add(spec)
class ManifestValidationError(spack.error.SpackError):

View File

@@ -59,7 +59,11 @@
import spack.util.lock as lk
import spack.util.spack_json as sjson
import spack.version as vn
from spack.directory_layout import DirectoryLayoutError, InconsistentInstallDirectoryError
from spack.directory_layout import (
DirectoryLayout,
DirectoryLayoutError,
InconsistentInstallDirectoryError,
)
from spack.error import SpackError
from spack.util.crypto import bit_length
@@ -203,12 +207,12 @@ class InstallRecord:
def __init__(
self,
spec: "spack.spec.Spec",
path: str,
path: Optional[str],
installed: bool,
ref_count: int = 0,
explicit: bool = False,
installation_time: Optional[float] = None,
deprecated_for: Optional["spack.spec.Spec"] = None,
deprecated_for: Optional[str] = None,
in_buildcache: bool = False,
origin=None,
):
@@ -595,9 +599,11 @@ class Database:
def __init__(
self,
root: str,
*,
upstream_dbs: Optional[List["Database"]] = None,
is_upstream: bool = False,
lock_cfg: LockConfiguration = DEFAULT_LOCK_CFG,
layout: Optional[DirectoryLayout] = None,
) -> None:
"""Database for Spack installations.
@@ -620,6 +626,7 @@ def __init__(
"""
self.root = root
self.database_directory = os.path.join(self.root, _DB_DIRNAME)
self.layout = layout
# Set up layout of database files within the db dir
self._index_path = os.path.join(self.database_directory, "index.json")
@@ -664,14 +671,6 @@ def __init__(
self.upstream_dbs = list(upstream_dbs) if upstream_dbs else []
# whether there was an error at the start of a read transaction
self._error = None
# For testing: if this is true, an exception is thrown when missing
# dependencies are detected (rather than just printing a warning
# message)
self._fail_when_missing_deps = False
self._write_transaction_impl = lk.WriteTransaction
self._read_transaction_impl = lk.ReadTransaction
@@ -774,7 +773,13 @@ def query_local_by_spec_hash(self, hash_key):
with self.read_transaction():
return self._data.get(hash_key, None)
def _assign_dependencies(self, spec_reader, hash_key, installs, data):
def _assign_dependencies(
self,
spec_reader: Type["spack.spec.SpecfileReaderBase"],
hash_key: str,
installs: dict,
data: Dict[str, InstallRecord],
):
# Add dependencies from other records in the install DB to
# form a full spec.
spec = data[hash_key].spec
@@ -787,26 +792,20 @@ def _assign_dependencies(self, spec_reader, hash_key, installs, data):
for dname, dhash, dtypes, _, virtuals in spec_reader.read_specfile_dep_specs(
yaml_deps
):
# It is important that we always check upstream installations
# in the same order, and that we always check the local
# installation first: if a downstream Spack installs a package
# then dependents in that installation could be using it.
# If a hash is installed locally and upstream, there isn't
# enough information to determine which one a local package
# depends on, so the convention ensures that this isn't an
# issue.
upstream, record = self.query_by_spec_hash(dhash, data=data)
# It is important that we always check upstream installations in the same order,
# and that we always check the local installation first: if a downstream Spack
# installs a package then dependents in that installation could be using it. If a
# hash is installed locally and upstream, there isn't enough information to
# determine which one a local package depends on, so the convention ensures that
# this isn't an issue.
_, record = self.query_by_spec_hash(dhash, data=data)
child = record.spec if record else None
if not child:
msg = "Missing dependency not in database: " "%s needs %s-%s" % (
spec.cformat("{name}{/hash:7}"),
dname,
dhash[:7],
tty.warn(
f"Missing dependency not in database: "
f"{spec.cformat('{name}{/hash:7}')} needs {dname}-{dhash[:7]}"
)
if self._fail_when_missing_deps:
raise MissingDependenciesError(msg)
tty.warn(msg)
continue
spec._add_dependency(child, depflag=dt.canonicalize(dtypes), virtuals=virtuals)
@@ -846,7 +845,7 @@ def check(cond, msg):
):
tty.warn(f"Spack database version changed from {version} to {_DB_VERSION}. Upgrading.")
self.reindex(spack.store.STORE.layout)
self.reindex()
installs = dict(
(k, v.to_dict(include_fields=self._record_fields)) for k, v in self._data.items()
)
@@ -873,8 +872,8 @@ def invalid_record(hash_key, error):
# (i.e., its specs are a true Merkle DAG, unlike most specs.)
# Pass 1: Iterate through database and build specs w/o dependencies
data = {}
installed_prefixes = set()
data: Dict[str, InstallRecord] = {}
installed_prefixes: Set[str] = set()
for hash_key, rec in installs.items():
try:
# This constructs a spec DAG from the list of all installs
@@ -911,7 +910,7 @@ def invalid_record(hash_key, error):
self._data = data
self._installed_prefixes = installed_prefixes
def reindex(self, directory_layout):
def reindex(self):
"""Build database index from scratch based on a directory layout.
Locks the DB if it isn't locked already.
@@ -926,105 +925,116 @@ def _read_suppress_error():
if os.path.isfile(self._index_path):
self._read_from_file(self._index_path)
except CorruptDatabaseError as e:
self._error = e
tty.warn(f"Reindexing corrupt database, error was: {e}")
self._data = {}
self._installed_prefixes = set()
transaction = lk.WriteTransaction(
self.lock, acquire=_read_suppress_error, release=self._write
)
with transaction:
if self._error:
tty.warn("Spack database was corrupt. Will rebuild. Error was:", str(self._error))
self._error = None
old_data = self._data
old_installed_prefixes = self._installed_prefixes
with lk.WriteTransaction(self.lock, acquire=_read_suppress_error, release=self._write):
old_installed_prefixes, self._installed_prefixes = self._installed_prefixes, set()
old_data, self._data = self._data, {}
try:
self._construct_from_directory_layout(directory_layout, old_data)
self._reindex(old_data)
except BaseException:
# If anything explodes, restore old data, skip write.
self._data = old_data
self._installed_prefixes = old_installed_prefixes
raise
def _construct_entry_from_directory_layout(
self, directory_layout, old_data, spec, deprecator=None
):
# Try to recover explicit value from old DB, but
# default it to True if DB was corrupt. This is
# just to be conservative in case a command like
# "autoremove" is run by the user after a reindex.
tty.debug("RECONSTRUCTING FROM SPEC.YAML: {0}".format(spec))
explicit = True
inst_time = os.stat(spec.prefix).st_ctime
if old_data is not None:
old_info = old_data.get(spec.dag_hash())
if old_info is not None:
explicit = old_info.explicit
inst_time = old_info.installation_time
def _reindex(self, old_data: Dict[str, InstallRecord]):
# Specs on the file system are the source of truth for record.spec. The old database values
# if available are the source of truth for the rest of the record.
assert self.layout, "Database layout must be set to reindex"
extra_args = {"explicit": explicit, "installation_time": inst_time}
self._add(spec, directory_layout, **extra_args)
if deprecator:
self._deprecate(spec, deprecator)
specs_from_fs = self.layout.all_specs()
deprecated_for = self.layout.deprecated_for(specs_from_fs)
def _construct_from_directory_layout(self, directory_layout, old_data):
# Read first the `spec.yaml` files in the prefixes. They should be
# considered authoritative with respect to DB reindexing, as
# entries in the DB may be corrupted in a way that still makes
# them readable. If we considered DB entries authoritative
# instead, we would perpetuate errors over a reindex.
with directory_layout.disable_upstream_check():
# Initialize data in the reconstructed DB
self._data = {}
self._installed_prefixes = set()
known_specs: List[spack.spec.Spec] = [
*specs_from_fs,
*(deprecated for _, deprecated in deprecated_for),
*(rec.spec for rec in old_data.values()),
]
# Start inspecting the installed prefixes
processed_specs = set()
upstream_hashes = {
dag_hash for upstream in self.upstream_dbs for dag_hash in upstream._data
}
upstream_hashes.difference_update(spec.dag_hash() for spec in known_specs)
for spec in directory_layout.all_specs():
self._construct_entry_from_directory_layout(directory_layout, old_data, spec)
processed_specs.add(spec)
def create_node(edge: spack.spec.DependencySpec, is_upstream: bool):
if is_upstream:
return
for spec, deprecator in directory_layout.all_deprecated_specs():
self._construct_entry_from_directory_layout(
directory_layout, old_data, spec, deprecator
self._data[edge.spec.dag_hash()] = InstallRecord(
spec=edge.spec.copy(deps=False),
path=edge.spec.external_path if edge.spec.external else None,
installed=edge.spec.external,
)
# Store all nodes of known specs, excluding ones found in upstreams
tr.traverse_breadth_first_with_visitor(
known_specs,
tr.CoverNodesVisitor(
NoUpstreamVisitor(upstream_hashes, create_node), key=tr.by_dag_hash
),
)
# Store the prefix and other information for specs were found on the file system
for s in specs_from_fs:
record = self._data[s.dag_hash()]
record.path = s.prefix
record.installed = True
record.explicit = True # conservative assumption
record.installation_time = os.stat(s.prefix).st_ctime
# Deprecate specs
for new, old in deprecated_for:
self._data[old.dag_hash()].deprecated_for = new.dag_hash()
# Copy data we have from the old database
for old_record in old_data.values():
record = self._data[old_record.spec.dag_hash()]
record.explicit = old_record.explicit
record.installation_time = old_record.installation_time
record.origin = old_record.origin
record.deprecated_for = old_record.deprecated_for
# Warn when the spec has been removed from the file system (i.e. it was not detected)
if not record.installed and old_record.installed:
tty.warn(
f"Spec {old_record.spec.short_spec} was marked installed in the database "
"but was not found on the file system. It is now marked as missing."
)
processed_specs.add(spec)
for key, entry in old_data.items():
# We already took care of this spec using
# `spec.yaml` from its prefix.
if entry.spec in processed_specs:
msg = "SKIPPING RECONSTRUCTION FROM OLD DB: {0}"
msg += " [already reconstructed from spec.yaml]"
tty.debug(msg.format(entry.spec))
continue
def create_edge(edge: spack.spec.DependencySpec, is_upstream: bool):
if not edge.parent:
return
parent_record = self._data[edge.parent.dag_hash()]
if is_upstream:
upstream, child_record = self.query_by_spec_hash(edge.spec.dag_hash())
assert upstream and child_record, "Internal error: upstream spec not found"
else:
child_record = self._data[edge.spec.dag_hash()]
parent_record.spec._add_dependency(
child_record.spec, depflag=edge.depflag, virtuals=edge.virtuals
)
# If we arrived here it very likely means that
# we have external specs that are not dependencies
# of other specs. This may be the case for externally
# installed compilers or externally installed
# applications.
tty.debug("RECONSTRUCTING FROM OLD DB: {0}".format(entry.spec))
try:
layout = None if entry.spec.external else directory_layout
kwargs = {
"spec": entry.spec,
"directory_layout": layout,
"explicit": entry.explicit,
"installation_time": entry.installation_time,
}
self._add(**kwargs)
processed_specs.add(entry.spec)
except Exception as e:
# Something went wrong, so the spec was not restored
# from old data
tty.debug(e)
# Then store edges
tr.traverse_breadth_first_with_visitor(
known_specs,
tr.CoverEdgesVisitor(
NoUpstreamVisitor(upstream_hashes, create_edge), key=tr.by_dag_hash
),
)
self._check_ref_counts()
# Finally update the ref counts
for record in self._data.values():
for dep in record.spec.dependencies(deptype=_TRACKED_DEPENDENCIES):
dep_record = self._data.get(dep.dag_hash())
if dep_record: # dep might be upstream
dep_record.ref_count += 1
if record.deprecated_for:
self._data[record.deprecated_for].ref_count += 1
self._check_ref_counts()
def _check_ref_counts(self):
"""Ensure consistency of reference counts in the DB.
@@ -1033,7 +1043,7 @@ def _check_ref_counts(self):
Does no locking.
"""
counts = {}
counts: Dict[str, int] = {}
for key, rec in self._data.items():
counts.setdefault(key, 0)
for dep in rec.spec.dependencies(deptype=_TRACKED_DEPENDENCIES):
@@ -1117,29 +1127,23 @@ def _read(self):
def _add(
self,
spec,
directory_layout=None,
explicit=False,
installation_time=None,
allow_missing=False,
spec: "spack.spec.Spec",
explicit: bool = False,
installation_time: Optional[float] = None,
allow_missing: bool = False,
):
"""Add an install record for this spec to the database.
Assumes spec is installed in ``directory_layout.path_for_spec(spec)``.
Also ensures dependencies are present and updated in the DB as
either installed or missing.
Also ensures dependencies are present and updated in the DB as either installed or missing.
Args:
spec (spack.spec.Spec): spec to be added
directory_layout: layout of the spec installation
spec: spec to be added
explicit:
Possible values: True, False, any
A spec that was installed following a specific user
request is marked as explicit. If instead it was
pulled-in as a dependency of a user requested spec
it's considered implicit.
A spec that was installed following a specific user request is marked as explicit.
If instead it was pulled-in as a dependency of a user requested spec it's
considered implicit.
installation_time:
Date and time of installation
@@ -1150,48 +1154,42 @@ def _add(
raise NonConcreteSpecAddError("Specs added to DB must be concrete.")
key = spec.dag_hash()
spec_pkg_hash = spec._package_hash
spec_pkg_hash = spec._package_hash # type: ignore[attr-defined]
upstream, record = self.query_by_spec_hash(key)
if upstream:
return
# Retrieve optional arguments
installation_time = installation_time or _now()
for edge in spec.edges_to_dependencies(depflag=_TRACKED_DEPENDENCIES):
if edge.spec.dag_hash() in self._data:
continue
# allow missing build-only deps. This prevents excessive
# warnings when a spec is installed, and its build dep
# is missing a build dep; there's no need to install the
# build dep's build dep first, and there's no need to warn
# about it missing.
dep_allow_missing = allow_missing or edge.depflag == dt.BUILD
self._add(
edge.spec,
directory_layout,
explicit=False,
installation_time=installation_time,
allow_missing=dep_allow_missing,
# allow missing build-only deps. This prevents excessive warnings when a spec is
# installed, and its build dep is missing a build dep; there's no need to install
# the build dep's build dep first, and there's no need to warn about it missing.
allow_missing=allow_missing or edge.depflag == dt.BUILD,
)
# Make sure the directory layout agrees whether the spec is installed
if not spec.external and directory_layout:
path = directory_layout.path_for_spec(spec)
if not spec.external and self.layout:
path = self.layout.path_for_spec(spec)
installed = False
try:
directory_layout.ensure_installed(spec)
self.layout.ensure_installed(spec)
installed = True
self._installed_prefixes.add(path)
except DirectoryLayoutError as e:
if not (allow_missing and isinstance(e, InconsistentInstallDirectoryError)):
msg = (
"{0} is being {1} in the database with prefix {2}, "
"but this directory does not contain an installation of "
"the spec, due to: {3}"
)
action = "updated" if key in self._data else "registered"
tty.warn(msg.format(spec.short_spec, action, path, str(e)))
tty.warn(
f"{spec.short_spec} is being {action} in the database with prefix {path}, "
"but this directory does not contain an installation of "
f"the spec, due to: {e}"
)
elif spec.external_path:
path = spec.external_path
installed = True
@@ -1202,23 +1200,27 @@ def _add(
if key not in self._data:
# Create a new install record with no deps initially.
new_spec = spec.copy(deps=False)
extra_args = {"explicit": explicit, "installation_time": installation_time}
# Commands other than 'spack install' may add specs to the DB,
# we can record the source of an installed Spec with 'origin'
if hasattr(spec, "origin"):
extra_args["origin"] = spec.origin
self._data[key] = InstallRecord(new_spec, path, installed, ref_count=0, **extra_args)
self._data[key] = InstallRecord(
new_spec,
path=path,
installed=installed,
ref_count=0,
explicit=explicit,
installation_time=installation_time,
origin=None if not hasattr(spec, "origin") else spec.origin,
)
# Connect dependencies from the DB to the new copy.
for dep in spec.edges_to_dependencies(depflag=_TRACKED_DEPENDENCIES):
dkey = dep.spec.dag_hash()
upstream, record = self.query_by_spec_hash(dkey)
assert record, f"Missing dependency {dep.spec.short_spec} in DB"
new_spec._add_dependency(record.spec, depflag=dep.depflag, virtuals=dep.virtuals)
if not upstream:
record.ref_count += 1
# Mark concrete once everything is built, and preserve
# the original hashes of concrete specs.
# Mark concrete once everything is built, and preserve the original hashes of concrete
# specs.
new_spec._mark_concrete()
new_spec._hash = key
new_spec._package_hash = spec_pkg_hash
@@ -1231,7 +1233,7 @@ def _add(
self._data[key].explicit = explicit
@_autospec
def add(self, spec, directory_layout, explicit=False):
def add(self, spec: "spack.spec.Spec", *, explicit: bool = False) -> None:
"""Add spec at path to database, locking and reading DB to sync.
``add()`` will lock and read from the DB on disk.
@@ -1240,9 +1242,9 @@ def add(self, spec, directory_layout, explicit=False):
# TODO: ensure that spec is concrete?
# Entire add is transactional.
with self.write_transaction():
self._add(spec, directory_layout, explicit=explicit)
self._add(spec, explicit=explicit)
def _get_matching_spec_key(self, spec, **kwargs):
def _get_matching_spec_key(self, spec: "spack.spec.Spec", **kwargs) -> str:
"""Get the exact spec OR get a single spec that matches."""
key = spec.dag_hash()
upstream, record = self.query_by_spec_hash(key)
@@ -1254,12 +1256,12 @@ def _get_matching_spec_key(self, spec, **kwargs):
return key
@_autospec
def get_record(self, spec, **kwargs):
def get_record(self, spec: "spack.spec.Spec", **kwargs) -> Optional[InstallRecord]:
key = self._get_matching_spec_key(spec, **kwargs)
upstream, record = self.query_by_spec_hash(key)
return record
def _decrement_ref_count(self, spec):
def _decrement_ref_count(self, spec: "spack.spec.Spec") -> None:
key = spec.dag_hash()
if key not in self._data:
@@ -1276,7 +1278,7 @@ def _decrement_ref_count(self, spec):
for dep in spec.dependencies(deptype=_TRACKED_DEPENDENCIES):
self._decrement_ref_count(dep)
def _increment_ref_count(self, spec):
def _increment_ref_count(self, spec: "spack.spec.Spec") -> None:
key = spec.dag_hash()
if key not in self._data:
@@ -1285,14 +1287,14 @@ def _increment_ref_count(self, spec):
rec = self._data[key]
rec.ref_count += 1
def _remove(self, spec):
def _remove(self, spec: "spack.spec.Spec") -> "spack.spec.Spec":
"""Non-locking version of remove(); does real work."""
key = self._get_matching_spec_key(spec)
rec = self._data[key]
# This install prefix is now free for other specs to use, even if the
# spec is only marked uninstalled.
if not rec.spec.external and rec.installed:
if not rec.spec.external and rec.installed and rec.path:
self._installed_prefixes.remove(rec.path)
if rec.ref_count > 0:
@@ -1316,7 +1318,7 @@ def _remove(self, spec):
return rec.spec
@_autospec
def remove(self, spec):
def remove(self, spec: "spack.spec.Spec") -> "spack.spec.Spec":
"""Removes a spec from the database. To be called on uninstall.
Reads the database, then:
@@ -1331,7 +1333,7 @@ def remove(self, spec):
with self.write_transaction():
return self._remove(spec)
def deprecator(self, spec):
def deprecator(self, spec: "spack.spec.Spec") -> Optional["spack.spec.Spec"]:
"""Return the spec that the given spec is deprecated for, or None"""
with self.read_transaction():
spec_key = self._get_matching_spec_key(spec)
@@ -1342,14 +1344,14 @@ def deprecator(self, spec):
else:
return None
def specs_deprecated_by(self, spec):
def specs_deprecated_by(self, spec: "spack.spec.Spec") -> List["spack.spec.Spec"]:
"""Return all specs deprecated in favor of the given spec"""
with self.read_transaction():
return [
rec.spec for rec in self._data.values() if rec.deprecated_for == spec.dag_hash()
]
def _deprecate(self, spec, deprecator):
def _deprecate(self, spec: "spack.spec.Spec", deprecator: "spack.spec.Spec") -> None:
spec_key = self._get_matching_spec_key(spec)
spec_rec = self._data[spec_key]
@@ -1367,17 +1369,17 @@ def _deprecate(self, spec, deprecator):
self._data[spec_key] = spec_rec
@_autospec
def mark(self, spec, key, value):
def mark(self, spec: "spack.spec.Spec", key, value) -> None:
"""Mark an arbitrary record on a spec."""
with self.write_transaction():
return self._mark(spec, key, value)
def _mark(self, spec, key, value):
def _mark(self, spec: "spack.spec.Spec", key, value) -> None:
record = self._data[self._get_matching_spec_key(spec)]
setattr(record, key, value)
@_autospec
def deprecate(self, spec, deprecator):
def deprecate(self, spec: "spack.spec.Spec", deprecator: "spack.spec.Spec") -> None:
"""Marks a spec as deprecated in favor of its deprecator"""
with self.write_transaction():
return self._deprecate(spec, deprecator)
@@ -1385,16 +1387,16 @@ def deprecate(self, spec, deprecator):
@_autospec
def installed_relatives(
self,
spec,
direction="children",
transitive=True,
spec: "spack.spec.Spec",
direction: str = "children",
transitive: bool = True,
deptype: Union[dt.DepFlag, dt.DepTypes] = dt.ALL,
):
) -> Set["spack.spec.Spec"]:
"""Return installed specs related to this one."""
if direction not in ("parents", "children"):
raise ValueError("Invalid direction: %s" % direction)
relatives = set()
relatives: Set[spack.spec.Spec] = set()
for spec in self.query(spec):
if transitive:
to_add = spec.traverse(direction=direction, root=False, deptype=deptype)
@@ -1405,17 +1407,13 @@ def installed_relatives(
for relative in to_add:
hash_key = relative.dag_hash()
upstream, record = self.query_by_spec_hash(hash_key)
_, record = self.query_by_spec_hash(hash_key)
if not record:
reltype = "Dependent" if direction == "parents" else "Dependency"
msg = "Inconsistent state! %s %s of %s not in DB" % (
reltype,
hash_key,
spec.dag_hash(),
tty.warn(
f"Inconsistent state: "
f"{'dependent' if direction == 'parents' else 'dependency'} {hash_key} of "
f"{spec.dag_hash()} not in DB"
)
if self._fail_when_missing_deps:
raise MissingDependenciesError(msg)
tty.warn(msg)
continue
if not record.installed:
@@ -1425,7 +1423,7 @@ def installed_relatives(
return relatives
@_autospec
def installed_extensions_for(self, extendee_spec):
def installed_extensions_for(self, extendee_spec: "spack.spec.Spec"):
"""Returns the specs of all packages that extend the given spec"""
for spec in self.query():
if spec.package.extends(extendee_spec):
@@ -1684,7 +1682,7 @@ def unused_specs(
self,
root_hashes: Optional[Container[str]] = None,
deptype: Union[dt.DepFlag, dt.DepTypes] = dt.LINK | dt.RUN,
) -> "List[spack.spec.Spec]":
) -> List["spack.spec.Spec"]:
"""Return all specs that are currently installed but not needed by root specs.
By default, roots are all explicit specs in the database. If a set of root
@@ -1728,6 +1726,33 @@ def update_explicit(self, spec, explicit):
rec.explicit = explicit
class NoUpstreamVisitor:
"""Gives edges to upstream specs, but does follow edges from upstream specs."""
def __init__(
self,
upstream_hashes: Set[str],
on_visit: Callable[["spack.spec.DependencySpec", bool], None],
):
self.upstream_hashes = upstream_hashes
self.on_visit = on_visit
def accept(self, item: tr.EdgeAndDepth) -> bool:
self.on_visit(item.edge, self.is_upstream(item))
return True
def is_upstream(self, item: tr.EdgeAndDepth) -> bool:
return item.edge.spec.dag_hash() in self.upstream_hashes
def neighbors(self, item: tr.EdgeAndDepth):
# Prune edges from upstream nodes, only follow database tracked dependencies
return (
[]
if self.is_upstream(item)
else item.edge.spec.edges_to_dependencies(depflag=_TRACKED_DEPENDENCIES)
)
class UpstreamDatabaseLockingError(SpackError):
"""Raised when an operation would need to lock an upstream database"""

View File

@@ -2,17 +2,11 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from .common import (
DetectedPackage,
executable_prefix,
set_virtuals_nonbuildable,
update_configuration,
)
from .common import executable_prefix, set_virtuals_nonbuildable, update_configuration
from .path import by_path, executables_in_path
from .test import detection_tests
__all__ = [
"DetectedPackage",
"by_path",
"executables_in_path",
"executable_prefix",

View File

@@ -6,9 +6,9 @@
function to update packages.yaml given a list of detected packages.
Ideally, each detection method should be placed in a specific subpackage
and implement at least a function that returns a list of DetectedPackage
objects. The update in packages.yaml can then be done using the function
provided here.
and implement at least a function that returns a list of specs.
The update in packages.yaml can then be done using the function provided here.
The module also contains other functions that might be useful across different
detection mechanisms.
@@ -17,9 +17,10 @@
import itertools
import os
import os.path
import pathlib
import re
import sys
from typing import Dict, List, NamedTuple, Optional, Set, Tuple, Union
from typing import Dict, List, Optional, Set, Tuple, Union
import llnl.util.tty
@@ -30,25 +31,6 @@
import spack.util.windows_registry
class DetectedPackage(NamedTuple):
"""Information on a package that has been detected."""
#: Spec that was detected
spec: spack.spec.Spec
#: Prefix of the spec
prefix: str
def __reduce__(self):
return DetectedPackage.restore, (str(self.spec), self.prefix, self.spec.extra_attributes)
@staticmethod
def restore(
spec_str: str, prefix: str, extra_attributes: Optional[Dict[str, str]]
) -> "DetectedPackage":
spec = spack.spec.Spec.from_detection(spec_str=spec_str, extra_attributes=extra_attributes)
return DetectedPackage(spec=spec, prefix=prefix)
def _externals_in_packages_yaml() -> Set[spack.spec.Spec]:
"""Returns all the specs mentioned as externals in packages.yaml"""
packages_yaml = spack.config.get("packages")
@@ -63,7 +45,7 @@ def _externals_in_packages_yaml() -> Set[spack.spec.Spec]:
def _pkg_config_dict(
external_pkg_entries: List[DetectedPackage],
external_pkg_entries: List["spack.spec.Spec"],
) -> Dict[str, Union[bool, List[Dict[str, ExternalEntryType]]]]:
"""Generate a package specific config dict according to the packages.yaml schema.
@@ -83,22 +65,19 @@ def _pkg_config_dict(
pkg_dict = spack.util.spack_yaml.syaml_dict()
pkg_dict["externals"] = []
for e in external_pkg_entries:
if not _spec_is_valid(e.spec):
if not _spec_is_valid(e):
continue
external_items: List[Tuple[str, ExternalEntryType]] = [
("spec", str(e.spec)),
("prefix", e.prefix),
("spec", str(e)),
("prefix", pathlib.Path(e.external_path).as_posix()),
]
if e.spec.external_modules:
external_items.append(("modules", e.spec.external_modules))
if e.external_modules:
external_items.append(("modules", e.external_modules))
if e.spec.extra_attributes:
if e.extra_attributes:
external_items.append(
(
"extra_attributes",
spack.util.spack_yaml.syaml_dict(e.spec.extra_attributes.items()),
)
("extra_attributes", spack.util.spack_yaml.syaml_dict(e.extra_attributes.items()))
)
# external_items.extend(e.spec.extra_attributes.items())
@@ -136,10 +115,10 @@ def path_to_dict(search_paths: List[str]):
# entry overrides later entries
for search_path in reversed(search_paths):
try:
for lib in os.listdir(search_path):
lib_path = os.path.join(search_path, lib)
if llnl.util.filesystem.is_readable_file(lib_path):
path_to_lib[lib_path] = lib
with os.scandir(search_path) as entries:
path_to_lib.update(
{entry.path: entry.name for entry in entries if entry.is_file()}
)
except OSError as e:
msg = f"cannot scan '{search_path}' for external software: {str(e)}"
llnl.util.tty.debug(msg)
@@ -219,33 +198,32 @@ def library_prefix(library_dir: str) -> str:
def update_configuration(
detected_packages: Dict[str, List[DetectedPackage]],
detected_packages: Dict[str, List["spack.spec.Spec"]],
scope: Optional[str] = None,
buildable: bool = True,
) -> List[spack.spec.Spec]:
"""Add the packages passed as arguments to packages.yaml
Args:
detected_packages: list of DetectedPackage objects to be added
detected_packages: list of specs to be added
scope: configuration scope where to add the detected packages
buildable: whether the detected packages are buildable or not
"""
predefined_external_specs = _externals_in_packages_yaml()
pkg_to_cfg, all_new_specs = {}, []
for package_name, entries in detected_packages.items():
new_entries = [e for e in entries if (e.spec not in predefined_external_specs)]
new_entries = [s for s in entries if s not in predefined_external_specs]
pkg_config = _pkg_config_dict(new_entries)
external_entries = pkg_config.get("externals", [])
assert not isinstance(external_entries, bool), "unexpected value for external entry"
all_new_specs.extend([spack.spec.Spec(x["spec"]) for x in external_entries])
all_new_specs.extend(new_entries)
if buildable is False:
pkg_config["buildable"] = False
pkg_to_cfg[package_name] = pkg_config
pkgs_cfg = spack.config.get("packages", scope=scope)
pkgs_cfg = spack.config.merge_yaml(pkgs_cfg, pkg_to_cfg)
spack.config.set("packages", pkgs_cfg, scope=scope)

View File

@@ -12,7 +12,7 @@
import re
import sys
import warnings
from typing import Dict, List, Optional, Set, Tuple, Type
from typing import Dict, Iterable, List, Optional, Set, Tuple, Type
import llnl.util.filesystem
import llnl.util.lang
@@ -24,7 +24,6 @@
import spack.util.ld_so_conf
from .common import (
DetectedPackage,
WindowsCompilerExternalPaths,
WindowsKitExternalPaths,
_convert_to_iterable,
@@ -62,7 +61,7 @@ def common_windows_package_paths(pkg_cls=None) -> List[str]:
def file_identifier(path):
s = os.stat(path)
return (s.st_dev, s.st_ino)
return s.st_dev, s.st_ino
def executables_in_path(path_hints: List[str]) -> Dict[str, str]:
@@ -80,6 +79,8 @@ def executables_in_path(path_hints: List[str]) -> Dict[str, str]:
constructed based on the PATH environment variable.
"""
search_paths = llnl.util.filesystem.search_paths_for_executables(*path_hints)
# Make use we don't doubly list /usr/lib and /lib etc
search_paths = list(llnl.util.lang.dedupe(search_paths, key=file_identifier))
return path_to_dict(search_paths)
@@ -187,7 +188,7 @@ def libraries_in_windows_paths(path_hints: Optional[List[str]] = None) -> Dict[s
return path_to_dict(search_paths)
def _group_by_prefix(paths: Set[str]) -> Dict[str, Set[str]]:
def _group_by_prefix(paths: List[str]) -> Dict[str, Set[str]]:
groups = collections.defaultdict(set)
for p in paths:
groups[os.path.dirname(p)].add(p)
@@ -227,7 +228,7 @@ def prefix_from_path(self, *, path: str) -> str:
def detect_specs(
self, *, pkg: Type["spack.package_base.PackageBase"], paths: List[str]
) -> List[DetectedPackage]:
) -> List["spack.spec.Spec"]:
"""Given a list of files matching the search patterns, returns a list of detected specs.
Args:
@@ -243,7 +244,9 @@ def detect_specs(
return []
result = []
for candidate_path, items_in_prefix in sorted(_group_by_prefix(set(paths)).items()):
for candidate_path, items_in_prefix in _group_by_prefix(
llnl.util.lang.dedupe(paths)
).items():
# TODO: multiple instances of a package can live in the same
# prefix, and a package implementation can return multiple specs
# for one prefix, but without additional details (e.g. about the
@@ -291,27 +294,25 @@ def detect_specs(
warnings.warn(msg)
continue
if spec.external_path:
prefix = spec.external_path
if not spec.external_path:
spec.external_path = prefix
result.append(DetectedPackage(spec=spec, prefix=prefix))
result.append(spec)
return result
def find(
self, *, pkg_name: str, initial_guess: Optional[List[str]] = None
) -> List[DetectedPackage]:
self, *, pkg_name: str, repository, initial_guess: Optional[List[str]] = None
) -> List["spack.spec.Spec"]:
"""For a given package, returns a list of detected specs.
Args:
pkg_name: package being detected
initial_guess: initial list of paths to search from the caller
if None, default paths are searched. If this
is an empty list, nothing will be searched.
repository: repository to retrieve the package
initial_guess: initial list of paths to search from the caller if None, default paths
are searched. If this is an empty list, nothing will be searched.
"""
import spack.repo
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
pkg_cls = repository.get_pkg_class(pkg_name)
patterns = self.search_patterns(pkg=pkg_cls)
if not patterns:
return []
@@ -335,13 +336,10 @@ def search_patterns(self, *, pkg: Type["spack.package_base.PackageBase"]) -> Lis
def candidate_files(self, *, patterns: List[str], paths: List[str]) -> List[str]:
executables_by_path = executables_in_path(path_hints=paths)
patterns = [re.compile(x) for x in patterns]
result = []
for compiled_re in patterns:
for path, exe in executables_by_path.items():
if compiled_re.search(exe):
result.append(path)
return list(sorted(set(result)))
joined_pattern = re.compile(r"|".join(patterns))
result = [path for path, exe in executables_by_path.items() if joined_pattern.search(exe)]
result.sort()
return result
def prefix_from_path(self, *, path: str) -> str:
result = executable_prefix(path)
@@ -385,11 +383,11 @@ def prefix_from_path(self, *, path: str) -> str:
def by_path(
packages_to_search: List[str],
packages_to_search: Iterable[str],
*,
path_hints: Optional[List[str]] = None,
max_workers: Optional[int] = None,
) -> Dict[str, List[DetectedPackage]]:
) -> Dict[str, List["spack.spec.Spec"]]:
"""Return the list of packages that have been detected on the system, keyed by
unqualified package name.
@@ -399,19 +397,28 @@ def by_path(
path_hints: initial list of paths to be searched
max_workers: maximum number of workers to search for packages in parallel
"""
import spack.repo
# TODO: Packages should be able to define both .libraries and .executables in the future
# TODO: determine_spec_details should get all relevant libraries and executables in one call
executables_finder, libraries_finder = ExecutablesFinder(), LibrariesFinder()
detected_specs_by_package: Dict[str, Tuple[concurrent.futures.Future, ...]] = {}
result = collections.defaultdict(list)
repository = spack.repo.PATH.ensure_unwrapped()
with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executor:
for pkg in packages_to_search:
executable_future = executor.submit(
executables_finder.find, pkg_name=pkg, initial_guess=path_hints
executables_finder.find,
pkg_name=pkg,
initial_guess=path_hints,
repository=repository,
)
library_future = executor.submit(
libraries_finder.find, pkg_name=pkg, initial_guess=path_hints
libraries_finder.find,
pkg_name=pkg,
initial_guess=path_hints,
repository=repository,
)
detected_specs_by_package[pkg] = executable_future, library_future

View File

@@ -68,7 +68,7 @@ def execute(self) -> List[spack.spec.Spec]:
with self._mock_layout() as path_hints:
entries = by_path([self.test.pkg_name], path_hints=path_hints)
_, unqualified_name = spack.repo.partition_package_name(self.test.pkg_name)
specs = set(x.spec for x in entries[unqualified_name])
specs = set(entries[unqualified_name])
return list(specs)
@contextlib.contextmanager
@@ -104,7 +104,9 @@ def _create_executable_scripts(self, mock_executables: MockExecutables) -> List[
@property
def expected_specs(self) -> List[spack.spec.Spec]:
return [
spack.spec.Spec.from_detection(item.spec, extra_attributes=item.extra_attributes)
spack.spec.Spec.from_detection(
item.spec, external_path=self.tmpdir.name, extra_attributes=item.extra_attributes
)
for item in self.test.results
]

View File

@@ -32,10 +32,9 @@ class OpenMpi(Package):
"""
import collections
import collections.abc
import functools
import os.path
import re
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Set, Tuple, Union
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, Union
import llnl.util.lang
import llnl.util.tty.color
@@ -48,6 +47,7 @@ class OpenMpi(Package):
import spack.util.crypto
import spack.variant
from spack.dependency import Dependency
from spack.directives_meta import DirectiveError, DirectiveMeta
from spack.fetch_strategy import from_kwargs
from spack.resource import Resource
from spack.version import (
@@ -80,22 +80,6 @@ class OpenMpi(Package):
"redistribute",
]
#: These are variant names used by Spack internally; packages can't use them
reserved_names = [
"arch",
"architecture",
"dev_path",
"namespace",
"operating_system",
"os",
"patches",
"platform",
"target",
]
#: Names of possible directives. This list is mostly populated using the @directive decorator.
#: Some directives leverage others and in that case are not automatically added.
directive_names = ["build_system"]
_patch_order_index = 0
@@ -155,219 +139,6 @@ def _make_when_spec(value: WhenType) -> Optional["spack.spec.Spec"]:
return spack.spec.Spec(value)
class DirectiveMeta(type):
"""Flushes the directives that were temporarily stored in the staging
area into the package.
"""
# Set of all known directives
_directive_dict_names: Set[str] = set()
_directives_to_be_executed: List[str] = []
_when_constraints_from_context: List[str] = []
_default_args: List[dict] = []
def __new__(cls, name, bases, attr_dict):
# Initialize the attribute containing the list of directives
# to be executed. Here we go reversed because we want to execute
# commands:
# 1. in the order they were defined
# 2. following the MRO
attr_dict["_directives_to_be_executed"] = []
for base in reversed(bases):
try:
directive_from_base = base._directives_to_be_executed
attr_dict["_directives_to_be_executed"].extend(directive_from_base)
except AttributeError:
# The base class didn't have the required attribute.
# Continue searching
pass
# De-duplicates directives from base classes
attr_dict["_directives_to_be_executed"] = [
x for x in llnl.util.lang.dedupe(attr_dict["_directives_to_be_executed"])
]
# Move things to be executed from module scope (where they
# are collected first) to class scope
if DirectiveMeta._directives_to_be_executed:
attr_dict["_directives_to_be_executed"].extend(
DirectiveMeta._directives_to_be_executed
)
DirectiveMeta._directives_to_be_executed = []
return super(DirectiveMeta, cls).__new__(cls, name, bases, attr_dict)
def __init__(cls, name, bases, attr_dict):
# The instance is being initialized: if it is a package we must ensure
# that the directives are called to set it up.
if "spack.pkg" in cls.__module__:
# Ensure the presence of the dictionaries associated with the directives.
# All dictionaries are defaultdicts that create lists for missing keys.
for d in DirectiveMeta._directive_dict_names:
setattr(cls, d, {})
# Lazily execute directives
for directive in cls._directives_to_be_executed:
directive(cls)
# Ignore any directives executed *within* top-level
# directives by clearing out the queue they're appended to
DirectiveMeta._directives_to_be_executed = []
super(DirectiveMeta, cls).__init__(name, bases, attr_dict)
@staticmethod
def push_to_context(when_spec):
"""Add a spec to the context constraints."""
DirectiveMeta._when_constraints_from_context.append(when_spec)
@staticmethod
def pop_from_context():
"""Pop the last constraint from the context"""
return DirectiveMeta._when_constraints_from_context.pop()
@staticmethod
def push_default_args(default_args):
"""Push default arguments"""
DirectiveMeta._default_args.append(default_args)
@staticmethod
def pop_default_args():
"""Pop default arguments"""
return DirectiveMeta._default_args.pop()
@staticmethod
def directive(dicts=None):
"""Decorator for Spack directives.
Spack directives allow you to modify a package while it is being
defined, e.g. to add version or dependency information. Directives
are one of the key pieces of Spack's package "language", which is
embedded in python.
Here's an example directive:
.. code-block:: python
@directive(dicts='versions')
version(pkg, ...):
...
This directive allows you write:
.. code-block:: python
class Foo(Package):
version(...)
The ``@directive`` decorator handles a couple things for you:
1. Adds the class scope (pkg) as an initial parameter when
called, like a class method would. This allows you to modify
a package from within a directive, while the package is still
being defined.
2. It automatically adds a dictionary called "versions" to the
package so that you can refer to pkg.versions.
The ``(dicts='versions')`` part ensures that ALL packages in Spack
will have a ``versions`` attribute after they're constructed, and
that if no directive actually modified it, it will just be an
empty dict.
This is just a modular way to add storage attributes to the
Package class, and it's how Spack gets information from the
packages to the core.
"""
global directive_names
if isinstance(dicts, str):
dicts = (dicts,)
if not isinstance(dicts, collections.abc.Sequence):
message = "dicts arg must be list, tuple, or string. Found {0}"
raise TypeError(message.format(type(dicts)))
# Add the dictionary names if not already there
DirectiveMeta._directive_dict_names |= set(dicts)
# This decorator just returns the directive functions
def _decorator(decorated_function):
directive_names.append(decorated_function.__name__)
@functools.wraps(decorated_function)
def _wrapper(*args, **_kwargs):
# First merge default args with kwargs
kwargs = dict()
for default_args in DirectiveMeta._default_args:
kwargs.update(default_args)
kwargs.update(_kwargs)
# Inject when arguments from the context
if DirectiveMeta._when_constraints_from_context:
# Check that directives not yet supporting the when= argument
# are not used inside the context manager
if decorated_function.__name__ == "version":
msg = (
'directive "{0}" cannot be used within a "when"'
' context since it does not support a "when=" '
"argument"
)
msg = msg.format(decorated_function.__name__)
raise DirectiveError(msg)
when_constraints = [
spack.spec.Spec(x) for x in DirectiveMeta._when_constraints_from_context
]
if kwargs.get("when"):
when_constraints.append(spack.spec.Spec(kwargs["when"]))
when_spec = spack.spec.merge_abstract_anonymous_specs(*when_constraints)
kwargs["when"] = when_spec
# If any of the arguments are executors returned by a
# directive passed as an argument, don't execute them
# lazily. Instead, let the called directive handle them.
# This allows nested directive calls in packages. The
# caller can return the directive if it should be queued.
def remove_directives(arg):
directives = DirectiveMeta._directives_to_be_executed
if isinstance(arg, (list, tuple)):
# Descend into args that are lists or tuples
for a in arg:
remove_directives(a)
else:
# Remove directives args from the exec queue
remove = next((d for d in directives if d is arg), None)
if remove is not None:
directives.remove(remove)
# Nasty, but it's the best way I can think of to avoid
# side effects if directive results are passed as args
remove_directives(args)
remove_directives(list(kwargs.values()))
# A directive returns either something that is callable on a
# package or a sequence of them
result = decorated_function(*args, **kwargs)
# ...so if it is not a sequence make it so
values = result
if not isinstance(values, collections.abc.Sequence):
values = (values,)
DirectiveMeta._directives_to_be_executed.extend(values)
# wrapped function returns same result as original so
# that we can nest directives
return result
return _wrapper
return _decorator
SubmoduleCallback = Callable[["spack.package_base.PackageBase"], Union[str, List[str], bool]]
directive = DirectiveMeta.directive
@@ -846,7 +617,7 @@ def format_error(msg, pkg):
msg += " @*r{{[{0}, variant '{1}']}}"
return llnl.util.tty.color.colorize(msg.format(pkg.name, name))
if name in reserved_names:
if name in spack.variant.reserved_names:
def _raise_reserved_name(pkg):
msg = "The name '%s' is reserved by Spack" % name
@@ -1110,10 +881,6 @@ def _execute_languages(pkg: "spack.package_base.PackageBase"):
return _execute_languages
class DirectiveError(spack.error.SpackError):
"""This is raised when something is wrong with a package directive."""
class DependencyError(DirectiveError):
"""This is raised when a dependency specification is invalid."""

View File

@@ -0,0 +1,234 @@
# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import collections.abc
import functools
from typing import List, Set
import llnl.util.lang
import spack.error
import spack.spec
#: Names of possible directives. This list is mostly populated using the @directive decorator.
#: Some directives leverage others and in that case are not automatically added.
directive_names = ["build_system"]
class DirectiveMeta(type):
"""Flushes the directives that were temporarily stored in the staging
area into the package.
"""
# Set of all known directives
_directive_dict_names: Set[str] = set()
_directives_to_be_executed: List[str] = []
_when_constraints_from_context: List[str] = []
_default_args: List[dict] = []
def __new__(cls, name, bases, attr_dict):
# Initialize the attribute containing the list of directives
# to be executed. Here we go reversed because we want to execute
# commands:
# 1. in the order they were defined
# 2. following the MRO
attr_dict["_directives_to_be_executed"] = []
for base in reversed(bases):
try:
directive_from_base = base._directives_to_be_executed
attr_dict["_directives_to_be_executed"].extend(directive_from_base)
except AttributeError:
# The base class didn't have the required attribute.
# Continue searching
pass
# De-duplicates directives from base classes
attr_dict["_directives_to_be_executed"] = [
x for x in llnl.util.lang.dedupe(attr_dict["_directives_to_be_executed"])
]
# Move things to be executed from module scope (where they
# are collected first) to class scope
if DirectiveMeta._directives_to_be_executed:
attr_dict["_directives_to_be_executed"].extend(
DirectiveMeta._directives_to_be_executed
)
DirectiveMeta._directives_to_be_executed = []
return super(DirectiveMeta, cls).__new__(cls, name, bases, attr_dict)
def __init__(cls, name, bases, attr_dict):
# The instance is being initialized: if it is a package we must ensure
# that the directives are called to set it up.
if "spack.pkg" in cls.__module__:
# Ensure the presence of the dictionaries associated with the directives.
# All dictionaries are defaultdicts that create lists for missing keys.
for d in DirectiveMeta._directive_dict_names:
setattr(cls, d, {})
# Lazily execute directives
for directive in cls._directives_to_be_executed:
directive(cls)
# Ignore any directives executed *within* top-level
# directives by clearing out the queue they're appended to
DirectiveMeta._directives_to_be_executed = []
super(DirectiveMeta, cls).__init__(name, bases, attr_dict)
@staticmethod
def push_to_context(when_spec):
"""Add a spec to the context constraints."""
DirectiveMeta._when_constraints_from_context.append(when_spec)
@staticmethod
def pop_from_context():
"""Pop the last constraint from the context"""
return DirectiveMeta._when_constraints_from_context.pop()
@staticmethod
def push_default_args(default_args):
"""Push default arguments"""
DirectiveMeta._default_args.append(default_args)
@staticmethod
def pop_default_args():
"""Pop default arguments"""
return DirectiveMeta._default_args.pop()
@staticmethod
def directive(dicts=None):
"""Decorator for Spack directives.
Spack directives allow you to modify a package while it is being
defined, e.g. to add version or dependency information. Directives
are one of the key pieces of Spack's package "language", which is
embedded in python.
Here's an example directive:
.. code-block:: python
@directive(dicts='versions')
version(pkg, ...):
...
This directive allows you write:
.. code-block:: python
class Foo(Package):
version(...)
The ``@directive`` decorator handles a couple things for you:
1. Adds the class scope (pkg) as an initial parameter when
called, like a class method would. This allows you to modify
a package from within a directive, while the package is still
being defined.
2. It automatically adds a dictionary called "versions" to the
package so that you can refer to pkg.versions.
The ``(dicts='versions')`` part ensures that ALL packages in Spack
will have a ``versions`` attribute after they're constructed, and
that if no directive actually modified it, it will just be an
empty dict.
This is just a modular way to add storage attributes to the
Package class, and it's how Spack gets information from the
packages to the core.
"""
global directive_names
if isinstance(dicts, str):
dicts = (dicts,)
if not isinstance(dicts, collections.abc.Sequence):
message = "dicts arg must be list, tuple, or string. Found {0}"
raise TypeError(message.format(type(dicts)))
# Add the dictionary names if not already there
DirectiveMeta._directive_dict_names |= set(dicts)
# This decorator just returns the directive functions
def _decorator(decorated_function):
directive_names.append(decorated_function.__name__)
@functools.wraps(decorated_function)
def _wrapper(*args, **_kwargs):
# First merge default args with kwargs
kwargs = dict()
for default_args in DirectiveMeta._default_args:
kwargs.update(default_args)
kwargs.update(_kwargs)
# Inject when arguments from the context
if DirectiveMeta._when_constraints_from_context:
# Check that directives not yet supporting the when= argument
# are not used inside the context manager
if decorated_function.__name__ == "version":
msg = (
'directive "{0}" cannot be used within a "when"'
' context since it does not support a "when=" '
"argument"
)
msg = msg.format(decorated_function.__name__)
raise DirectiveError(msg)
when_constraints = [
spack.spec.Spec(x) for x in DirectiveMeta._when_constraints_from_context
]
if kwargs.get("when"):
when_constraints.append(spack.spec.Spec(kwargs["when"]))
when_spec = spack.spec.merge_abstract_anonymous_specs(*when_constraints)
kwargs["when"] = when_spec
# If any of the arguments are executors returned by a
# directive passed as an argument, don't execute them
# lazily. Instead, let the called directive handle them.
# This allows nested directive calls in packages. The
# caller can return the directive if it should be queued.
def remove_directives(arg):
directives = DirectiveMeta._directives_to_be_executed
if isinstance(arg, (list, tuple)):
# Descend into args that are lists or tuples
for a in arg:
remove_directives(a)
else:
# Remove directives args from the exec queue
remove = next((d for d in directives if d is arg), None)
if remove is not None:
directives.remove(remove)
# Nasty, but it's the best way I can think of to avoid
# side effects if directive results are passed as args
remove_directives(args)
remove_directives(list(kwargs.values()))
# A directive returns either something that is callable on a
# package or a sequence of them
result = decorated_function(*args, **kwargs)
# ...so if it is not a sequence make it so
values = result
if not isinstance(values, collections.abc.Sequence):
values = (values,)
DirectiveMeta._directives_to_be_executed.extend(values)
# wrapped function returns same result as original so
# that we can nest directives
return result
return _wrapper
return _decorator
class DirectiveError(spack.error.SpackError):
"""This is raised when something is wrong with a package directive."""

View File

@@ -4,17 +4,14 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import errno
import glob
import os
import posixpath
import re
import shutil
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import List, Optional, Tuple
import llnl.util.filesystem as fs
import llnl.util.tty as tty
from llnl.util.symlink import readlink
import spack.config
@@ -23,13 +20,8 @@
import spack.util.spack_json as sjson
from spack.error import SpackError
# Note: Posixpath is used here as opposed to
# os.path.join due to spack.spec.Spec.format
# requiring forward slash path seperators at this stage
default_projections = {
"all": posixpath.join(
"{architecture}", "{compiler.name}-{compiler.version}", "{name}-{version}-{hash}"
)
"all": "{architecture}/{compiler.name}-{compiler.version}/{name}-{version}-{hash}"
}
@@ -39,6 +31,42 @@ def _check_concrete(spec):
raise ValueError("Specs passed to a DirectoryLayout must be concrete!")
def _get_spec(prefix: str) -> Optional["spack.spec.Spec"]:
"""Returns a spec if the prefix contains a spec file in the .spack subdir"""
for f in ("spec.json", "spec.yaml"):
try:
return spack.spec.Spec.from_specfile(os.path.join(prefix, ".spack", f))
except Exception:
continue
return None
def specs_from_metadata_dirs(root: str) -> List["spack.spec.Spec"]:
stack = [root]
specs = []
while stack:
prefix = stack.pop()
spec = _get_spec(prefix)
if spec:
spec.prefix = prefix
specs.append(spec)
continue
try:
scandir = os.scandir(prefix)
except OSError:
continue
with scandir as entries:
for entry in entries:
if entry.is_dir(follow_symlinks=False):
stack.append(entry.path)
return specs
class DirectoryLayout:
"""A directory layout is used to associate unique paths with specs.
Different installations are going to want different layouts for their
@@ -152,20 +180,9 @@ def read_spec(self, path):
def spec_file_path(self, spec):
"""Gets full path to spec file"""
_check_concrete(spec)
# Attempts to convert to JSON if possible.
# Otherwise just returns the YAML.
yaml_path = os.path.join(self.metadata_path(spec), self._spec_file_name_yaml)
json_path = os.path.join(self.metadata_path(spec), self.spec_file_name)
if os.path.exists(yaml_path) and fs.can_write_to_dir(yaml_path):
self.write_spec(spec, json_path)
try:
os.remove(yaml_path)
except OSError as err:
tty.debug("Could not remove deprecated {0}".format(yaml_path))
tty.debug(err)
elif os.path.exists(yaml_path):
return yaml_path
return json_path
return yaml_path if os.path.exists(yaml_path) else json_path
def deprecated_file_path(self, deprecated_spec, deprecator_spec=None):
"""Gets full path to spec file for deprecated spec
@@ -199,23 +216,7 @@ def deprecated_file_path(self, deprecated_spec, deprecator_spec=None):
deprecated_spec.dag_hash() + "_" + self.spec_file_name,
)
if os.path.exists(yaml_path) and fs.can_write_to_dir(yaml_path):
self.write_spec(deprecated_spec, json_path)
try:
os.remove(yaml_path)
except (IOError, OSError) as err:
tty.debug("Could not remove deprecated {0}".format(yaml_path))
tty.debug(err)
elif os.path.exists(yaml_path):
return yaml_path
return json_path
@contextmanager
def disable_upstream_check(self):
self.check_upstream = False
yield
self.check_upstream = True
return yaml_path if os.path.exists(yaml_path) else json_path
def metadata_path(self, spec):
return os.path.join(spec.prefix, self.metadata_dir)
@@ -271,53 +272,6 @@ def ensure_installed(self, spec):
"Spec file in %s does not match hash!" % spec_file_path
)
def all_specs(self):
if not os.path.isdir(self.root):
return []
specs = []
for _, path_scheme in self.projections.items():
path_elems = ["*"] * len(path_scheme.split(posixpath.sep))
# NOTE: Does not validate filename extension; should happen later
path_elems += [self.metadata_dir, "spec.json"]
pattern = os.path.join(self.root, *path_elems)
spec_files = glob.glob(pattern)
if not spec_files: # we're probably looking at legacy yaml...
path_elems += [self.metadata_dir, "spec.yaml"]
pattern = os.path.join(self.root, *path_elems)
spec_files = glob.glob(pattern)
specs.extend([self.read_spec(s) for s in spec_files])
return specs
def all_deprecated_specs(self):
if not os.path.isdir(self.root):
return []
deprecated_specs = set()
for _, path_scheme in self.projections.items():
path_elems = ["*"] * len(path_scheme.split(posixpath.sep))
# NOTE: Does not validate filename extension; should happen later
path_elems += [
self.metadata_dir,
self.deprecated_dir,
"*_spec.*",
] # + self.spec_file_name]
pattern = os.path.join(self.root, *path_elems)
spec_files = glob.glob(pattern)
get_depr_spec_file = lambda x: os.path.join(
os.path.dirname(os.path.dirname(x)), self.spec_file_name
)
deprecated_specs |= set(
(self.read_spec(s), self.read_spec(get_depr_spec_file(s))) for s in spec_files
)
return deprecated_specs
def specs_by_hash(self):
by_hash = {}
for spec in self.all_specs():
by_hash[spec.dag_hash()] = spec
return by_hash
def path_for_spec(self, spec):
"""Return absolute path from the root to a directory for the spec."""
_check_concrete(spec)
@@ -383,6 +337,35 @@ def remove_install_directory(self, spec, deprecated=False):
raise e
path = os.path.dirname(path)
def all_specs(self) -> List["spack.spec.Spec"]:
"""Returns a list of all specs detected in self.root, detected by `.spack` directories.
Their prefix is set to the directory containing the `.spack` directory. Note that these
specs may follow a different layout than the current layout if it was changed after
installation."""
return specs_from_metadata_dirs(self.root)
def deprecated_for(
self, specs: List["spack.spec.Spec"]
) -> List[Tuple["spack.spec.Spec", "spack.spec.Spec"]]:
"""Returns a list of tuples of specs (new, old) where new is deprecated for old"""
spec_with_deprecated = []
for spec in specs:
try:
deprecated = os.scandir(
os.path.join(str(spec.prefix), self.metadata_dir, self.deprecated_dir)
)
except OSError:
continue
with deprecated as entries:
for entry in entries:
try:
deprecated_spec = spack.spec.Spec.from_specfile(entry.path)
spec_with_deprecated.append((spec, deprecated_spec))
except Exception:
continue
return spec_with_deprecated
class DirectoryLayoutError(SpackError):
"""Superclass for directory layout errors."""

View File

@@ -58,9 +58,8 @@
from spack.installer import PackageInstaller
from spack.schema.env import TOP_LEVEL_KEY
from spack.spec import Spec
from spack.spec_list import InvalidSpecConstraintError, SpecList
from spack.spec_list import SpecList
from spack.util.path import substitute_path_variables
from spack.variant import UnknownVariantError
#: environment variable used to indicate the active environment
spack_env_var = "SPACK_ENV"
@@ -1214,7 +1213,6 @@ def scope_name(self):
def include_concrete_envs(self):
"""Copy and save the included envs' specs internally"""
lockfile_meta = None
root_hash_seen = set()
concrete_hash_seen = set()
self.included_concrete_spec_data = {}
@@ -1225,37 +1223,26 @@ def include_concrete_envs(self):
raise SpackEnvironmentError(f"Unable to find env at {env_path}")
env = Environment(env_path)
with open(env.lock_path) as f:
lockfile_as_dict = env._read_lockfile(f)
# Lockfile_meta must match each env and use at least format version 5
if lockfile_meta is None:
lockfile_meta = lockfile_as_dict["_meta"]
elif lockfile_meta != lockfile_as_dict["_meta"]:
raise SpackEnvironmentError("All lockfile _meta values must match")
elif lockfile_meta["lockfile-version"] < 5:
raise SpackEnvironmentError("The lockfile format must be at version 5 or higher")
self.included_concrete_spec_data[env_path] = {"roots": [], "concrete_specs": {}}
# Copy unique root specs from env
self.included_concrete_spec_data[env_path] = {"roots": []}
for root_dict in lockfile_as_dict["roots"]:
for root_dict in env._concrete_roots_dict():
if root_dict["hash"] not in root_hash_seen:
self.included_concrete_spec_data[env_path]["roots"].append(root_dict)
root_hash_seen.add(root_dict["hash"])
# Copy unique concrete specs from env
for concrete_spec in lockfile_as_dict["concrete_specs"]:
if concrete_spec not in concrete_hash_seen:
self.included_concrete_spec_data[env_path].update(
{"concrete_specs": lockfile_as_dict["concrete_specs"]}
for dag_hash, spec_details in env._concrete_specs_dict().items():
if dag_hash not in concrete_hash_seen:
self.included_concrete_spec_data[env_path]["concrete_specs"].update(
{dag_hash: spec_details}
)
concrete_hash_seen.add(concrete_spec)
concrete_hash_seen.add(dag_hash)
if "include_concrete" in lockfile_as_dict.keys():
self.included_concrete_spec_data[env_path]["include_concrete"] = lockfile_as_dict[
"include_concrete"
]
# Copy transitive include data
transitive = env.included_concrete_spec_data
if transitive:
self.included_concrete_spec_data[env_path]["include_concrete"] = transitive
self._read_lockfile_dict(self._to_lockfile_dict())
self.write()
@@ -1637,16 +1624,15 @@ def _concretize_separately(self, tests=False):
# Concretize any new user specs that we haven't concretized yet
args, root_specs, i = [], [], 0
for uspec, uspec_constraints in zip(self.user_specs, self.user_specs.specs_as_constraints):
for uspec in self.user_specs:
if uspec not in old_concretized_user_specs:
root_specs.append(uspec)
args.append((i, [str(x) for x in uspec_constraints], tests))
args.append((i, str(uspec), tests))
i += 1
# Ensure we don't try to bootstrap clingo in parallel
if spack.config.get("config:concretizer", "clingo") == "clingo":
with spack.bootstrap.ensure_bootstrap_configuration():
spack.bootstrap.ensure_clingo_importable_or_raise()
with spack.bootstrap.ensure_bootstrap_configuration():
spack.bootstrap.ensure_clingo_importable_or_raise()
# Ensure all the indexes have been built or updated, since
# otherwise the processes in the pool may timeout on waiting
@@ -1657,7 +1643,7 @@ def _concretize_separately(self, tests=False):
# Ensure we have compilers in compilers.yaml to avoid that
# processes try to write the config file in parallel
_ = spack.compilers.get_compiler_config(spack.config.CONFIG, init_config=True)
_ = spack.compilers.all_compilers_config(spack.config.CONFIG)
# Early return if there is nothing to do
if len(args) == 0:
@@ -2174,16 +2160,23 @@ def _get_environment_specs(self, recurse_dependencies=True):
return specs
def _to_lockfile_dict(self):
"""Create a dictionary to store a lockfile for this environment."""
def _concrete_specs_dict(self):
concrete_specs = {}
for s in traverse.traverse_nodes(self.specs_by_hash.values(), key=traverse.by_dag_hash):
spec_dict = s.node_dict_with_hashes(hash=ht.dag_hash)
# Assumes no legacy formats, since this was just created.
spec_dict[ht.dag_hash.name] = s.dag_hash()
concrete_specs[s.dag_hash()] = spec_dict
return concrete_specs
def _concrete_roots_dict(self):
hash_spec_list = zip(self.concretized_order, self.concretized_user_specs)
return [{"hash": h, "spec": str(s)} for h, s in hash_spec_list]
def _to_lockfile_dict(self):
"""Create a dictionary to store a lockfile for this environment."""
concrete_specs = self._concrete_specs_dict()
root_specs = self._concrete_roots_dict()
spack_dict = {"version": spack.spack_version}
spack_commit = spack.main.get_spack_commit()
@@ -2204,7 +2197,7 @@ def _to_lockfile_dict(self):
# spack version information
"spack": spack_dict,
# users specs + hashes are the 'roots' of the environment
"roots": [{"hash": h, "spec": str(s)} for h, s in hash_spec_list],
"roots": root_specs,
# Concrete specs by hash, including dependencies
"concrete_specs": concrete_specs,
}
@@ -2514,52 +2507,11 @@ def display_specs(specs):
print(tree_string)
def _concretize_from_constraints(spec_constraints, tests=False):
# Accept only valid constraints from list and concretize spec
# Get the named spec even if out of order
root_spec = [s for s in spec_constraints if s.name]
if len(root_spec) != 1:
m = "The constraints %s are not a valid spec " % spec_constraints
m += "concretization target. all specs must have a single name "
m += "constraint for concretization."
raise InvalidSpecConstraintError(m)
spec_constraints.remove(root_spec[0])
invalid_constraints = []
while True:
# Attach all anonymous constraints to one named spec
s = root_spec[0].copy()
for c in spec_constraints:
if c not in invalid_constraints:
s.constrain(c)
try:
return s.concretized(tests=tests)
except spack.spec.InvalidDependencyError as e:
invalid_deps_string = ["^" + d for d in e.invalid_deps]
invalid_deps = [
c
for c in spec_constraints
if any(c.satisfies(invd) for invd in invalid_deps_string)
]
if len(invalid_deps) != len(invalid_deps_string):
raise e
invalid_constraints.extend(invalid_deps)
except UnknownVariantError as e:
invalid_variants = e.unknown_variants
inv_variant_constraints = [
c for c in spec_constraints if any(name in c.variants for name in invalid_variants)
]
if len(inv_variant_constraints) != len(invalid_variants):
raise e
invalid_constraints.extend(inv_variant_constraints)
def _concretize_task(packed_arguments) -> Tuple[int, Spec, float]:
index, spec_constraints, tests = packed_arguments
spec_constraints = [Spec(x) for x in spec_constraints]
index, spec_str, tests = packed_arguments
with tty.SuppressOutput(msg_enabled=False):
start = time.time()
spec = _concretize_from_constraints(spec_constraints, tests)
spec = Spec(spec_str).concretized(tests=tests)
return index, spec, time.time() - start

View File

@@ -30,6 +30,7 @@
import shutil
import urllib.error
import urllib.parse
import urllib.request
from pathlib import PurePath
from typing import List, Optional
@@ -53,7 +54,7 @@
import spack.version
import spack.version.git_ref_lookup
from spack.util.compression import decompressor_for
from spack.util.executable import CommandNotFoundError, which
from spack.util.executable import CommandNotFoundError, Executable, which
#: List of all fetch strategies, created by FetchStrategy metaclass.
all_strategies = []
@@ -245,38 +246,30 @@ class URLFetchStrategy(FetchStrategy):
# these are checksum types. The generic 'checksum' is deprecated for
# specific hash names, but we need it for backward compatibility
optional_attrs = list(crypto.hashes.keys()) + ["checksum"]
optional_attrs = [*crypto.hashes.keys(), "checksum"]
def __init__(self, url=None, checksum=None, **kwargs):
def __init__(self, *, url: str, checksum: Optional[str] = None, **kwargs) -> None:
super().__init__(**kwargs)
# Prefer values in kwargs to the positionals.
self.url = kwargs.get("url", url)
self.url = url
self.mirrors = kwargs.get("mirrors", [])
# digest can be set as the first argument, or from an explicit
# kwarg by the hash name.
self.digest = kwargs.get("checksum", checksum)
self.digest: Optional[str] = checksum
for h in self.optional_attrs:
if h in kwargs:
self.digest = kwargs[h]
self.expand_archive = kwargs.get("expand", True)
self.extra_options = kwargs.get("fetch_options", {})
self._curl = None
self.extension = kwargs.get("extension", None)
if not self.url:
raise ValueError("URLFetchStrategy requires a url for fetching.")
self.expand_archive: bool = kwargs.get("expand", True)
self.extra_options: dict = kwargs.get("fetch_options", {})
self._curl: Optional[Executable] = None
self.extension: Optional[str] = kwargs.get("extension", None)
@property
def curl(self):
def curl(self) -> Executable:
if not self._curl:
try:
self._curl = which("curl", required=True)
except CommandNotFoundError as exc:
tty.error(str(exc))
self._curl = web_util.require_curl()
return self._curl
def source_id(self):
@@ -297,27 +290,23 @@ def candidate_urls(self):
@_needs_stage
def fetch(self):
if self.archive_file:
tty.debug("Already downloaded {0}".format(self.archive_file))
tty.debug(f"Already downloaded {self.archive_file}")
return
url = None
errors = []
errors: List[Exception] = []
for url in self.candidate_urls:
if not web_util.url_exists(url):
tty.debug("URL does not exist: " + url)
continue
try:
self._fetch_from_url(url)
break
except FailedDownloadError as e:
errors.append(str(e))
for msg in errors:
tty.debug(msg)
errors.extend(e.exceptions)
else:
raise FailedDownloadError(*errors)
if not self.archive_file:
raise FailedDownloadError(url)
raise FailedDownloadError(
RuntimeError(f"Missing archive {self.archive_file} after fetching")
)
def _fetch_from_url(self, url):
if spack.config.get("config:url_fetch_method") == "curl":
@@ -336,27 +325,28 @@ def _check_headers(self, headers):
@_needs_stage
def _fetch_urllib(self, url):
save_file = self.stage.save_filename
tty.msg("Fetching {0}".format(url))
# Run urllib but grab the mime type from the http headers
request = urllib.request.Request(url, headers={"User-Agent": web_util.SPACK_USER_AGENT})
try:
url, headers, response = web_util.read_from_url(url)
except web_util.SpackWebError as e:
response = web_util.urlopen(request)
except (TimeoutError, urllib.error.URLError) as e:
# clean up archive on failure.
if self.archive_file:
os.remove(self.archive_file)
if os.path.lexists(save_file):
os.remove(save_file)
msg = "urllib failed to fetch with error {0}".format(e)
raise FailedDownloadError(url, msg)
raise FailedDownloadError(e) from e
tty.msg(f"Fetching {url}")
if os.path.lexists(save_file):
os.remove(save_file)
with open(save_file, "wb") as _open_file:
shutil.copyfileobj(response, _open_file)
with open(save_file, "wb") as f:
shutil.copyfileobj(response, f)
self._check_headers(str(headers))
self._check_headers(str(response.headers))
@_needs_stage
def _fetch_curl(self, url):
@@ -365,7 +355,7 @@ def _fetch_curl(self, url):
if self.stage.save_filename:
save_file = self.stage.save_filename
partial_file = self.stage.save_filename + ".part"
tty.msg("Fetching {0}".format(url))
tty.msg(f"Fetching {url}")
if partial_file:
save_args = [
"-C",
@@ -405,8 +395,8 @@ def _fetch_curl(self, url):
try:
web_util.check_curl_code(curl.returncode)
except spack.error.FetchError as err:
raise spack.fetch_strategy.FailedDownloadError(url, str(err))
except spack.error.FetchError as e:
raise FailedDownloadError(e) from e
self._check_headers(headers)
@@ -473,7 +463,7 @@ def check(self):
"""Check the downloaded archive against a checksum digest.
No-op if this stage checks code out of a repository."""
if not self.digest:
raise NoDigestError("Attempt to check URLFetchStrategy with no digest.")
raise NoDigestError(f"Attempt to check {self.__class__.__name__} with no digest.")
verify_checksum(self.archive_file, self.digest)
@@ -484,8 +474,8 @@ def reset(self):
"""
if not self.archive_file:
raise NoArchiveFileError(
"Tried to reset URLFetchStrategy before fetching",
"Failed on reset() for URL %s" % self.url,
f"Tried to reset {self.__class__.__name__} before fetching",
f"Failed on reset() for URL{self.url}",
)
# Remove everything but the archive from the stage
@@ -498,14 +488,10 @@ def reset(self):
self.expand()
def __repr__(self):
url = self.url if self.url else "no url"
return "%s<%s>" % (self.__class__.__name__, url)
return f"{self.__class__.__name__}<{self.url}>"
def __str__(self):
if self.url:
return self.url
else:
return "[no url]"
return self.url
@fetcher
@@ -518,7 +504,7 @@ def fetch(self):
# check whether the cache file exists.
if not os.path.isfile(path):
raise NoCacheError("No cache of %s" % path)
raise NoCacheError(f"No cache of {path}")
# remove old symlink if one is there.
filename = self.stage.save_filename
@@ -528,8 +514,8 @@ def fetch(self):
# Symlink to local cached archive.
symlink(path, filename)
# Remove link if checksum fails, or subsequent fetchers
# will assume they don't need to download.
# Remove link if checksum fails, or subsequent fetchers will assume they don't need to
# download.
if self.digest:
try:
self.check()
@@ -538,12 +524,12 @@ def fetch(self):
raise
# Notify the user how we fetched.
tty.msg("Using cached archive: {0}".format(path))
tty.msg(f"Using cached archive: {path}")
class OCIRegistryFetchStrategy(URLFetchStrategy):
def __init__(self, url=None, checksum=None, **kwargs):
super().__init__(url, checksum, **kwargs)
def __init__(self, *, url: str, checksum: Optional[str] = None, **kwargs):
super().__init__(url=url, checksum=checksum, **kwargs)
self._urlopen = kwargs.get("_urlopen", spack.oci.opener.urlopen)
@@ -554,13 +540,13 @@ def fetch(self):
try:
response = self._urlopen(self.url)
except urllib.error.URLError as e:
except (TimeoutError, urllib.error.URLError) as e:
# clean up archive on failure.
if self.archive_file:
os.remove(self.archive_file)
if os.path.lexists(file):
os.remove(file)
raise FailedDownloadError(self.url, f"Failed to fetch {self.url}: {e}") from e
raise FailedDownloadError(e) from e
if os.path.lexists(file):
os.remove(file)
@@ -588,18 +574,18 @@ def __init__(self, **kwargs):
# Set a URL based on the type of fetch strategy.
self.url = kwargs.get(self.url_attr, None)
if not self.url:
raise ValueError("%s requires %s argument." % (self.__class__, self.url_attr))
raise ValueError(f"{self.__class__} requires {self.url_attr} argument.")
for attr in self.optional_attrs:
setattr(self, attr, kwargs.get(attr, None))
@_needs_stage
def check(self):
tty.debug("No checksum needed when fetching with {0}".format(self.url_attr))
tty.debug(f"No checksum needed when fetching with {self.url_attr}")
@_needs_stage
def expand(self):
tty.debug("Source fetched with %s is already expanded." % self.url_attr)
tty.debug(f"Source fetched with {self.url_attr} is already expanded.")
@_needs_stage
def archive(self, destination, *, exclude: Optional[str] = None):
@@ -619,10 +605,10 @@ def archive(self, destination, *, exclude: Optional[str] = None):
)
def __str__(self):
return "VCS: %s" % self.url
return f"VCS: {self.url}"
def __repr__(self):
return "%s<%s>" % (self.__class__, self.url)
return f"{self.__class__}<{self.url}>"
@fetcher
@@ -725,11 +711,17 @@ class GitFetchStrategy(VCSFetchStrategy):
"submodules",
"get_full_repo",
"submodules_delete",
"git_sparse_paths",
]
git_version_re = r"git version (\S+)"
def __init__(self, **kwargs):
self.commit: Optional[str] = None
self.tag: Optional[str] = None
self.branch: Optional[str] = None
# Discards the keywords in kwargs that may conflict with the next call
# to __init__
forwarded_args = copy.copy(kwargs)
@@ -740,6 +732,7 @@ def __init__(self, **kwargs):
self.submodules = kwargs.get("submodules", False)
self.submodules_delete = kwargs.get("submodules_delete", False)
self.get_full_repo = kwargs.get("get_full_repo", False)
self.git_sparse_paths = kwargs.get("git_sparse_paths", None)
@property
def git_version(self):
@@ -777,68 +770,71 @@ def git(self):
@property
def cachable(self):
return self.cache_enabled and bool(self.commit or self.tag)
return self.cache_enabled and bool(self.commit)
def source_id(self):
return self.commit or self.tag
# TODO: tree-hash would secure download cache and mirrors, commit only secures checkouts.
return self.commit
def mirror_id(self):
repo_ref = self.commit or self.tag or self.branch
if repo_ref:
if self.commit:
repo_path = urllib.parse.urlparse(self.url).path
result = os.path.sep.join(["git", repo_path, repo_ref])
result = os.path.sep.join(["git", repo_path, self.commit])
return result
def _repo_info(self):
args = ""
if self.commit:
args = " at commit {0}".format(self.commit)
args = f" at commit {self.commit}"
elif self.tag:
args = " at tag {0}".format(self.tag)
args = f" at tag {self.tag}"
elif self.branch:
args = " on branch {0}".format(self.branch)
args = f" on branch {self.branch}"
return "{0}{1}".format(self.url, args)
return f"{self.url}{args}"
@_needs_stage
def fetch(self):
if self.stage.expanded:
tty.debug("Already fetched {0}".format(self.stage.source_path))
tty.debug(f"Already fetched {self.stage.source_path}")
return
self.clone(commit=self.commit, branch=self.branch, tag=self.tag)
if self.git_sparse_paths:
self._sparse_clone_src()
else:
self._clone_src()
self.submodule_operations()
def clone(self, dest=None, commit=None, branch=None, tag=None, bare=False):
def bare_clone(self, dest: str) -> None:
"""
Clone a repository to a path.
Execute a bare clone for metadata only
This method handles cloning from git, but does not require a stage.
Arguments:
dest (str or None): The path into which the code is cloned. If None,
requires a stage and uses the stage's source path.
commit (str or None): A commit to fetch from the remote. Only one of
commit, branch, and tag may be non-None.
branch (str or None): A branch to fetch from the remote.
tag (str or None): A tag to fetch from the remote.
bare (bool): Execute a "bare" git clone (--bare option to git)
Requires a destination since bare cloning does not provide source
and shouldn't be used for staging.
"""
# Default to spack source path
dest = dest or self.stage.source_path
tty.debug("Cloning git repository: {0}".format(self._repo_info()))
tty.debug(f"Cloning git repository: {self._repo_info()}")
git = self.git
debug = spack.config.get("config:debug")
if bare:
# We don't need to worry about which commit/branch/tag is checked out
clone_args = ["clone", "--bare"]
if not debug:
clone_args.append("--quiet")
clone_args.extend([self.url, dest])
git(*clone_args)
elif commit:
# We don't need to worry about which commit/branch/tag is checked out
clone_args = ["clone", "--bare"]
if not debug:
clone_args.append("--quiet")
clone_args.extend([self.url, dest])
git(*clone_args)
def _clone_src(self) -> None:
"""Clone a repository to a path using git."""
# Default to spack source path
dest = self.stage.source_path
tty.debug(f"Cloning git repository: {self._repo_info()}")
git = self.git
debug = spack.config.get("config:debug")
if self.commit:
# Need to do a regular clone and check out everything if
# they asked for a particular commit.
clone_args = ["clone", self.url]
@@ -857,7 +853,7 @@ def clone(self, dest=None, commit=None, branch=None, tag=None, bare=False):
)
with working_dir(dest):
checkout_args = ["checkout", commit]
checkout_args = ["checkout", self.commit]
if not debug:
checkout_args.insert(1, "--quiet")
git(*checkout_args)
@@ -869,10 +865,10 @@ def clone(self, dest=None, commit=None, branch=None, tag=None, bare=False):
args.append("--quiet")
# If we want a particular branch ask for it.
if branch:
args.extend(["--branch", branch])
elif tag and self.git_version >= spack.version.Version("1.8.5.2"):
args.extend(["--branch", tag])
if self.branch:
args.extend(["--branch", self.branch])
elif self.tag and self.git_version >= spack.version.Version("1.8.5.2"):
args.extend(["--branch", self.tag])
# Try to be efficient if we're using a new enough git.
# This checks out only one branch's history
@@ -904,7 +900,7 @@ def clone(self, dest=None, commit=None, branch=None, tag=None, bare=False):
# For tags, be conservative and check them out AFTER
# cloning. Later git versions can do this with clone
# --branch, but older ones fail.
if tag and self.git_version < spack.version.Version("1.8.5.2"):
if self.tag and self.git_version < spack.version.Version("1.8.5.2"):
# pull --tags returns a "special" error code of 1 in
# older versions that we have to ignore.
# see: https://github.com/git/git/commit/19d122b
@@ -917,6 +913,79 @@ def clone(self, dest=None, commit=None, branch=None, tag=None, bare=False):
git(*pull_args, ignore_errors=1)
git(*co_args)
def _sparse_clone_src(self, **kwargs):
"""Use git's sparse checkout feature to clone portions of a git repository"""
dest = self.stage.source_path
git = self.git
if self.git_version < spack.version.Version("2.26.0"):
# technically this should be supported for 2.25, but bumping for OS issues
# see https://github.com/spack/spack/issues/45771
# code paths exist where the package is not set. Assure some indentifier for the
# package that was configured for sparse checkout exists in the error message
identifier = str(self.url)
if self.package:
identifier += f" ({self.package.name})"
tty.warn(
(
f"{identifier} is configured for git sparse-checkout "
"but the git version is too old to support sparse cloning. "
"Cloning the full repository instead."
)
)
self._clone_src()
else:
# default to depth=2 to allow for retention of some git properties
depth = kwargs.get("depth", 2)
needs_fetch = self.branch or self.tag
git_ref = self.branch or self.tag or self.commit
assert git_ref
clone_args = ["clone"]
if needs_fetch:
clone_args.extend(["--branch", git_ref])
if self.get_full_repo:
clone_args.append("--no-single-branch")
else:
clone_args.append("--single-branch")
clone_args.extend(
[f"--depth={depth}", "--no-checkout", "--filter=blob:none", self.url]
)
sparse_args = ["sparse-checkout", "set"]
if callable(self.git_sparse_paths):
sparse_args.extend(self.git_sparse_paths())
else:
sparse_args.extend([p for p in self.git_sparse_paths])
sparse_args.append("--cone")
checkout_args = ["checkout", git_ref]
if not spack.config.get("config:debug"):
clone_args.insert(1, "--quiet")
checkout_args.insert(1, "--quiet")
with temp_cwd():
git(*clone_args)
repo_name = get_single_file(".")
if self.stage:
self.stage.srcdir = repo_name
shutil.move(repo_name, dest)
with working_dir(dest):
git(*sparse_args)
git(*checkout_args)
def submodule_operations(self):
dest = self.stage.source_path
git = self.git
if self.submodules_delete:
with working_dir(dest):
for submodule_to_delete in self.submodules_delete:
@@ -969,7 +1038,7 @@ def protocol_supports_shallow_clone(self):
return not (self.url.startswith("http://") or self.url.startswith("/"))
def __str__(self):
return "[git] {0}".format(self._repo_info())
return f"[git] {self._repo_info()}"
@fetcher
@@ -1293,7 +1362,7 @@ def reset(self):
shutil.move(scrubbed, source_path)
def __str__(self):
return "[hg] %s" % self.url
return f"[hg] {self.url}"
@fetcher
@@ -1302,45 +1371,20 @@ class S3FetchStrategy(URLFetchStrategy):
url_attr = "s3"
def __init__(self, *args, **kwargs):
try:
super().__init__(*args, **kwargs)
except ValueError:
if not kwargs.get("url"):
raise ValueError("S3FetchStrategy requires a url for fetching.")
@_needs_stage
def fetch(self):
if self.archive_file:
tty.debug("Already downloaded {0}".format(self.archive_file))
return
parsed_url = urllib.parse.urlparse(self.url)
if parsed_url.scheme != "s3":
raise spack.error.FetchError("S3FetchStrategy can only fetch from s3:// urls.")
tty.debug("Fetching {0}".format(self.url))
basename = os.path.basename(parsed_url.path)
with working_dir(self.stage.path):
_, headers, stream = web_util.read_from_url(self.url)
with open(basename, "wb") as f:
shutil.copyfileobj(stream, f)
content_type = web_util.get_header(headers, "Content-type")
if content_type == "text/html":
warn_content_type_mismatch(self.archive_file or "the archive")
if self.stage.save_filename:
llnl.util.filesystem.rename(
os.path.join(self.stage.path, basename), self.stage.save_filename
if not self.url.startswith("s3://"):
raise spack.error.FetchError(
f"{self.__class__.__name__} can only fetch from s3:// urls."
)
if self.archive_file:
tty.debug(f"Already downloaded {self.archive_file}")
return
self._fetch_urllib(self.url)
if not self.archive_file:
raise FailedDownloadError(self.url)
raise FailedDownloadError(
RuntimeError(f"Missing archive {self.archive_file} after fetching")
)
@fetcher
@@ -1349,43 +1393,22 @@ class GCSFetchStrategy(URLFetchStrategy):
url_attr = "gs"
def __init__(self, *args, **kwargs):
try:
super().__init__(*args, **kwargs)
except ValueError:
if not kwargs.get("url"):
raise ValueError("GCSFetchStrategy requires a url for fetching.")
@_needs_stage
def fetch(self):
if not self.url.startswith("gs"):
raise spack.error.FetchError(
f"{self.__class__.__name__} can only fetch from gs:// urls."
)
if self.archive_file:
tty.debug("Already downloaded {0}".format(self.archive_file))
tty.debug(f"Already downloaded {self.archive_file}")
return
parsed_url = urllib.parse.urlparse(self.url)
if parsed_url.scheme != "gs":
raise spack.error.FetchError("GCSFetchStrategy can only fetch from gs:// urls.")
tty.debug("Fetching {0}".format(self.url))
basename = os.path.basename(parsed_url.path)
with working_dir(self.stage.path):
_, headers, stream = web_util.read_from_url(self.url)
with open(basename, "wb") as f:
shutil.copyfileobj(stream, f)
content_type = web_util.get_header(headers, "Content-type")
if content_type == "text/html":
warn_content_type_mismatch(self.archive_file or "the archive")
if self.stage.save_filename:
os.rename(os.path.join(self.stage.path, basename), self.stage.save_filename)
self._fetch_urllib(self.url)
if not self.archive_file:
raise FailedDownloadError(self.url)
raise FailedDownloadError(
RuntimeError(f"Missing archive {self.archive_file} after fetching")
)
@fetcher
@@ -1394,7 +1417,7 @@ class FetchAndVerifyExpandedFile(URLFetchStrategy):
as well as after expanding it."""
def __init__(self, url, archive_sha256: str, expanded_sha256: str):
super().__init__(url, archive_sha256)
super().__init__(url=url, checksum=archive_sha256)
self.expanded_sha256 = expanded_sha256
def expand(self):
@@ -1436,14 +1459,14 @@ def stable_target(fetcher):
return False
def from_url(url):
def from_url(url: str) -> URLFetchStrategy:
"""Given a URL, find an appropriate fetch strategy for it.
Currently just gives you a URLFetchStrategy that uses curl.
TODO: make this return appropriate fetch strategies for other
types of URLs.
"""
return URLFetchStrategy(url)
return URLFetchStrategy(url=url)
def from_kwargs(**kwargs):
@@ -1512,10 +1535,12 @@ def _check_version_attributes(fetcher, pkg, version):
def _extrapolate(pkg, version):
"""Create a fetcher from an extrapolated URL for this version."""
try:
return URLFetchStrategy(pkg.url_for_version(version), fetch_options=pkg.fetch_options)
return URLFetchStrategy(url=pkg.url_for_version(version), fetch_options=pkg.fetch_options)
except spack.package_base.NoURLError:
msg = "Can't extrapolate a URL for version %s " "because package %s defines no URLs"
raise ExtrapolationError(msg % (version, pkg.name))
raise ExtrapolationError(
f"Can't extrapolate a URL for version {version} because "
f"package {pkg.name} defines no URLs"
)
def _from_merged_attrs(fetcher, pkg, version):
@@ -1532,8 +1557,11 @@ def _from_merged_attrs(fetcher, pkg, version):
attrs["fetch_options"] = pkg.fetch_options
attrs.update(pkg.versions[version])
if fetcher.url_attr == "git" and hasattr(pkg, "submodules"):
attrs.setdefault("submodules", pkg.submodules)
if fetcher.url_attr == "git":
pkg_attr_list = ["submodules", "git_sparse_paths"]
for pkg_attr in pkg_attr_list:
if hasattr(pkg, pkg_attr):
attrs.setdefault(pkg_attr, getattr(pkg, pkg_attr))
return fetcher(**attrs)
@@ -1628,11 +1656,9 @@ def for_package_version(pkg, version=None):
raise InvalidArgsError(pkg, version, **args)
def from_url_scheme(url, *args, **kwargs):
def from_url_scheme(url: str, **kwargs) -> FetchStrategy:
"""Finds a suitable FetchStrategy by matching its url_attr with the scheme
in the given url."""
url = kwargs.get("url", url)
parsed_url = urllib.parse.urlparse(url, scheme="file")
scheme_mapping = kwargs.get("scheme_mapping") or {
@@ -1649,11 +1675,9 @@ def from_url_scheme(url, *args, **kwargs):
for fetcher in all_strategies:
url_attr = getattr(fetcher, "url_attr", None)
if url_attr and url_attr == scheme:
return fetcher(url, *args, **kwargs)
return fetcher(url=url, **kwargs)
raise ValueError(
'No FetchStrategy found for url with scheme: "{SCHEME}"'.format(SCHEME=parsed_url.scheme)
)
raise ValueError(f'No FetchStrategy found for url with scheme: "{parsed_url.scheme}"')
def from_list_url(pkg):
@@ -1678,7 +1702,9 @@ def from_list_url(pkg):
)
# construct a fetcher
return URLFetchStrategy(url_from_list, checksum, fetch_options=pkg.fetch_options)
return URLFetchStrategy(
url=url_from_list, checksum=checksum, fetch_options=pkg.fetch_options
)
except KeyError as e:
tty.debug(e)
tty.msg("Cannot find version %s in url_list" % pkg.version)
@@ -1706,10 +1732,10 @@ def store(self, fetcher, relative_dest):
mkdirp(os.path.dirname(dst))
fetcher.archive(dst)
def fetcher(self, target_path, digest, **kwargs):
def fetcher(self, target_path: str, digest: Optional[str], **kwargs) -> CacheURLFetchStrategy:
path = os.path.join(self.root, target_path)
url = url_util.path_to_file_url(path)
return CacheURLFetchStrategy(url, digest, **kwargs)
return CacheURLFetchStrategy(url=url, checksum=digest, **kwargs)
def destroy(self):
shutil.rmtree(self.root, ignore_errors=True)
@@ -1722,9 +1748,9 @@ class NoCacheError(spack.error.FetchError):
class FailedDownloadError(spack.error.FetchError):
"""Raised when a download fails."""
def __init__(self, url, msg=""):
super().__init__("Failed to fetch file from URL: %s" % url, msg)
self.url = url
def __init__(self, *exceptions: Exception):
super().__init__("Failed to download")
self.exceptions = exceptions
class NoArchiveFileError(spack.error.FetchError):

View File

@@ -37,6 +37,12 @@ def __call__(self, spec):
"""Run this hash on the provided spec."""
return spec.spec_hash(self)
def __repr__(self):
return (
f"SpecHashDescriptor(depflag={self.depflag!r}, "
f"package_hash={self.package_hash!r}, name={self.name!r}, override={self.override!r})"
)
#: Spack's deployment hash. Includes all inputs that can affect how a package is built.
dag_hash = SpecHashDescriptor(depflag=dt.BUILD | dt.LINK | dt.RUN, package_hash=True, name="hash")

Some files were not shown because too many files have changed in this diff Show More