Compare commits

..

1 Commits

Author SHA1 Message Date
Wouter Deconinck
bdcd817233 containers: update docs from centos:7 to almalinux:9 2024-07-04 19:40:24 -05:00
3976 changed files with 3211 additions and 14665 deletions

View File

@@ -12,7 +12,6 @@ updates:
interval: "daily"
# Requirements to run style checks
- package-ecosystem: "pip"
directories:
- "/.github/workflows/requirements/*"
directory: "/.github/workflows/style"
schedule:
interval: "daily"

View File

@@ -28,8 +28,8 @@ jobs:
run:
shell: ${{ matrix.system.shell }}
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: ${{inputs.python_version}}
- name: Install Python packages

View File

@@ -37,7 +37,7 @@ jobs:
make patch unzip which xz python3 python3-devel tree \
cmake bison
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- name: Bootstrap clingo
@@ -60,10 +60,10 @@ jobs:
run: |
brew install cmake bison tree
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: "3.12"
- name: Bootstrap clingo
@@ -71,14 +71,12 @@ jobs:
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 bootstrap disable github-actions-v0.5
spack bootstrap disable github-actions-v0.4
spack external find --not-buildable cmake bison
spack -d solve zlib
${{ env.VALIDATE_LAST_EXIT }}
tree ${{ env.USER_SCOPE_PARENT_DIR }}/.spack/bootstrap/store/
gnupg-sources:
@@ -96,7 +94,7 @@ jobs:
if: ${{ matrix.runner == 'ubuntu-latest' }}
run: sudo rm -rf $(command -v gpg gpg2 patchelf)
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- name: Bootstrap GnuPG
@@ -125,10 +123,10 @@ jobs:
run: |
sudo rm -rf $(which gpg) $(which gpg2) $(which patchelf)
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: |
3.8
@@ -154,7 +152,7 @@ jobs:
not_found=0
old_path="$PATH"
export PATH="$ver_dir:$PATH"
./bin/spack-tmpconfig -b ./.github/workflows/bin/bootstrap-test.sh
./bin/spack-tmpconfig -b ./.github/workflows/bootstrap-test.sh
export PATH="$old_path"
fi
fi
@@ -168,3 +166,4 @@ jobs:
source share/spack/setup-env.sh
spack -d gpg list
tree ~/.spack/bootstrap/store/

View File

@@ -55,7 +55,7 @@ jobs:
if: github.repository == 'spack/spack'
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81
id: docker_meta
@@ -76,7 +76,7 @@ jobs:
env:
SPACK_YAML_OS: "${{ matrix.dockerfile[2] }}"
run: |
.github/workflows/bin/generate_spack_yaml_containerize.sh
.github/workflows/generate_spack_yaml_containerize.sh
. share/spack/setup-env.sh
mkdir -p dockerfiles/${{ matrix.dockerfile[0] }}
spack containerize --last-stage=bootstrap | tee dockerfiles/${{ matrix.dockerfile[0] }}/Dockerfile
@@ -87,19 +87,19 @@ jobs:
fi
- name: Upload Dockerfile
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
with:
name: dockerfiles_${{ matrix.dockerfile[0] }}
path: dockerfiles
- name: Set up QEMU
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf
uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27
uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4
- name: Log in to GitHub Container Registry
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -107,13 +107,13 @@ jobs:
- name: Log in to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build & Deploy ${{ matrix.dockerfile[0] }}
uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445
uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c
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@0b2256b8c012f0828dc542b3febcab082c67f72b
uses: actions/upload-artifact/merge@65462800fd760344b1a7b4382951275a0abb4808
with:
name: dockerfiles
pattern: dockerfiles_*

View File

@@ -36,7 +36,7 @@ jobs:
core: ${{ steps.filter.outputs.core }}
packages: ${{ steps.filter.outputs.packages }}
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
if: ${{ github.event_name == 'push' }}
with:
fetch-depth: 0

8
.github/workflows/install_spack.sh vendored Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env sh
. share/spack/setup-env.sh
echo -e "config:\n build_jobs: 2" > etc/spack/config.yaml
spack config add "packages:all:target:[x86_64]"
spack compiler find
spack compiler info apple-clang
spack debug report
spack solve zlib

View File

@@ -14,10 +14,10 @@ jobs:
build-paraview-deps:
runs-on: windows-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: 3.9
- name: Install Python packages

View File

@@ -51,10 +51,10 @@ jobs:
on_develop: false
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: ${{ matrix.python-version }}
- name: Install System packages
@@ -72,7 +72,7 @@ jobs:
run: |
# Need this for the git tests to succeed.
git --version
. .github/workflows/bin/setup_git.sh
. .github/workflows/setup_git.sh
- name: Bootstrap clingo
if: ${{ matrix.concretizer == 'clingo' }}
env:
@@ -100,10 +100,10 @@ jobs:
shell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: '3.11'
- name: Install System packages
@@ -118,7 +118,7 @@ jobs:
run: |
# Need this for the git tests to succeed.
git --version
. .github/workflows/bin/setup_git.sh
. .github/workflows/setup_git.sh
- name: Run shell tests
env:
COVERAGE: true
@@ -141,13 +141,13 @@ jobs:
dnf install -y \
bzip2 curl file gcc-c++ gcc gcc-gfortran git gnupg2 gzip \
make patch tcl unzip which xz
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Setup repo and non-root user
run: |
git --version
git config --global --add safe.directory /__w/spack/spack
git fetch --unshallow
. .github/workflows/bin/setup_git.sh
. .github/workflows/setup_git.sh
useradd spack-test
chown -R spack-test .
- name: Run unit tests
@@ -160,10 +160,10 @@ jobs:
clingo-cffi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: '3.11'
- name: Install System packages
@@ -178,7 +178,7 @@ jobs:
run: |
# Need this for the git tests to succeed.
git --version
. .github/workflows/bin/setup_git.sh
. .github/workflows/setup_git.sh
- name: Run unit tests (full suite with coverage)
env:
COVERAGE: true
@@ -198,10 +198,10 @@ jobs:
os: [macos-13, macos-14]
python-version: ["3.11"]
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: ${{ matrix.python-version }}
- name: Install Python packages
@@ -217,7 +217,7 @@ jobs:
SPACK_TEST_PARALLEL: 4
run: |
git --version
. .github/workflows/bin/setup_git.sh
. .github/workflows/setup_git.sh
. share/spack/setup-env.sh
$(which spack) bootstrap disable spack-install
$(which spack) solve zlib
@@ -236,10 +236,10 @@ jobs:
powershell Invoke-Expression -Command "./share/spack/qa/windows_test_setup.ps1"; {0}
runs-on: windows-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: 3.9
- name: Install Python packages
@@ -247,7 +247,7 @@ jobs:
python -m pip install --upgrade pip pywin32 setuptools pytest-cov clingo
- name: Create local develop
run: |
./.github/workflows/bin/setup_git.ps1
./.github/workflows/setup_git.ps1
- name: Unit Test
run: |
spack unit-test -x --verbose --cov --cov-config=pyproject.toml

View File

@@ -18,15 +18,15 @@ jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: '3.11'
cache: 'pip'
- name: Install Python Packages
run: |
pip install --upgrade pip setuptools
pip install -r .github/workflows/requirements/style/requirements.txt
pip install -r .github/workflows/style/requirements.txt
- name: vermin (Spack's Core)
run: vermin --backport importlib --backport argparse --violations --backport typing -t=3.6- -vvv lib/spack/spack/ lib/spack/llnl/ bin/
- name: vermin (Repositories)
@@ -35,22 +35,22 @@ jobs:
style:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
fetch-depth: 0
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: '3.11'
cache: 'pip'
- name: Install Python packages
run: |
pip install --upgrade pip setuptools
pip install -r .github/workflows/requirements/style/requirements.txt
pip install -r .github/workflows/style/requirements.txt
- name: Setup git configuration
run: |
# Need this for the git tests to succeed.
git --version
. .github/workflows/bin/setup_git.sh
. .github/workflows/setup_git.sh
- name: Run style tests
run: |
share/spack/qa/run-style-tests
@@ -70,13 +70,13 @@ jobs:
dnf install -y \
bzip2 curl file gcc-c++ gcc gcc-gfortran git gnupg2 gzip \
make patch tcl unzip which xz
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Setup repo and non-root user
run: |
git --version
git config --global --add safe.directory /__w/spack/spack
git fetch --unshallow
. .github/workflows/bin/setup_git.sh
. .github/workflows/setup_git.sh
useradd spack-test
chown -R spack-test .
- name: Bootstrap Spack development environment

View File

@@ -0,0 +1,3 @@
packages:
iconv:
require: [libiconv]

View File

@@ -270,7 +270,7 @@ under the ``container`` attribute of environments:
# Sets the base images for the stages where Spack builds the
# software or where the software gets installed after being built..
images:
os: "centos:7"
os: "almalinux:9"
spack: develop
# Whether or not to strip binaries
@@ -321,32 +321,33 @@ following ``spack.yaml``:
container:
images:
os: centos:7
spack: 0.15.4
os: almalinux:9
spack: 0.22.0
uses ``spack/centos7:0.15.4`` and ``centos:7`` for the stages where the
uses ``spack/almalinux9:0.22.0`` and ``almalinux:9`` for the stages where the
software is respectively built and installed:
.. code-block:: docker
# Build stage with Spack pre-installed and ready to be used
FROM spack/centos7:0.15.4 as builder
FROM spack/almalinux9:0.22.0 AS builder
# What we want to install and how we want to install it
# is specified in a manifest file (spack.yaml)
RUN mkdir /opt/spack-environment \
&& (echo "spack:" \
&& echo " specs:" \
&& echo " - gromacs+mpi" \
&& echo " - mpich" \
&& echo " concretizer:" \
&& echo " unify: true" \
&& echo " config:" \
&& echo " install_tree: /opt/software" \
&& echo " view: /opt/view") > /opt/spack-environment/spack.yaml
RUN mkdir -p /opt/spack-environment && \
set -o noclobber \
&& (echo spack: \
&& echo ' specs:' \
&& echo ' - gromacs+mpi' \
&& echo ' - mpich' \
&& echo ' concretizer:' \
&& echo ' unify: true' \
&& echo ' config:' \
&& echo ' install_tree: /opt/software' \
&& echo ' view: /opt/views/view') > /opt/spack-environment/spack.yaml
[ ... ]
# Bare OS image to run the installed executables
FROM centos:7
FROM quay.io/almalinuxorg/almalinux:9
COPY --from=builder /opt/spack-environment /opt/spack-environment
COPY --from=builder /opt/software /opt/software

View File

@@ -931,84 +931,32 @@ This allows for a much-needed reduction in redundancy between packages
and constraints.
-----------------
Environment Views
-----------------
----------------
Filesystem Views
----------------
Spack Environments can have an associated filesystem view, which is a directory
with a more traditional structure ``<view>/bin``, ``<view>/lib``, ``<view>/include``
in which all files of the installed packages are linked.
By default a view is created for each environment, thanks to the ``view: true``
option in the ``spack.yaml`` manifest file:
.. code-block:: yaml
spack:
specs: [perl, python]
view: true
The view is created in a hidden directory ``.spack-env/view`` relative to the environment.
If you've used ``spack env activate``, you may have already interacted with this view. Spack
prepends its ``<view>/bin`` dir to ``PATH`` when the environment is activated, so that
you can directly run executables from all installed packages in the environment.
Views are highly customizable: you can control where they are put, modify their structure,
include and exclude specs, change how files are linked, and you can even generate multiple
views for a single environment.
Spack Environments can define filesystem views, which provide a direct access point
for software similar to the directory hierarchy that might exist under ``/usr/local``.
Filesystem views are updated every time the environment is written out to the lock
file ``spack.lock``, so the concrete environment and the view are always compatible.
The files of the view's installed packages are brought into the view by symbolic or
hard links, referencing the original Spack installation, or by copy.
.. _configuring_environment_views:
^^^^^^^^^^^^^^^^^^^^^^^^^^
Minimal view configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Configuration in ``spack.yaml``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The minimal configuration
.. code-block:: yaml
spack:
# ...
view: true
lets Spack generate a single view with default settings under the
``.spack-env/view`` directory of the environment.
Another short way to configure a view is to specify just where to put it:
.. code-block:: yaml
spack:
# ...
view: /path/to/view
Views can also be disabled by setting ``view: false``.
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Advanced view configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^
One or more **view descriptors** can be defined under ``view``, keyed by a name.
The example from the previous section with ``view: /path/to/view`` is equivalent
to defining a view descriptor named ``default`` with a ``root`` attribute:
.. code-block:: yaml
spack:
# ...
view:
default: # name of the view
root: /path/to/view # view descriptor attribute
The ``default`` view descriptor name is special: when you ``spack env activate`` your
environment, this view will be used to update (among other things) your ``PATH``
variable.
View descriptors must contain the root of the view, and optionally projections,
``select`` and ``exclude`` lists and link information via ``link`` and
The Spack Environment manifest file has a top-level keyword
``view``. Each entry under that heading is a **view descriptor**, headed
by a name. Any number of views may be defined under the ``view`` heading.
The view descriptor contains the root of the view, and
optionally the projections for the view, ``select`` and
``exclude`` lists for the view and link information via ``link`` and
``link_type``.
As a more advanced example, in the following manifest
For example, in the following manifest
file snippet we define a view named ``mpis``, rooted at
``/path/to/view`` in which all projections use the package name,
version, and compiler name to determine the path for a given
@@ -1053,10 +1001,59 @@ of ``hardlink`` or ``copy``.
when the environment is not activated, and linked libraries will be located
*outside* of the view thanks to rpaths.
There are two shorthands for environments with a single view. If the
environment at ``/path/to/env`` has a single view, with a root at
``/path/to/env/.spack-env/view``, with default selection and exclusion
and the default projection, we can put ``view: True`` in the
environment manifest. Similarly, if the environment has a view with a
different root, but default selection, exclusion, and projections, the
manifest can say ``view: /path/to/view``. These views are
automatically named ``default``, so that
.. code-block:: yaml
spack:
# ...
view: True
is equivalent to
.. code-block:: yaml
spack:
# ...
view:
default:
root: .spack-env/view
and
.. code-block:: yaml
spack:
# ...
view: /path/to/view
is equivalent to
.. code-block:: yaml
spack:
# ...
view:
default:
root: /path/to/view
By default, Spack environments are configured with ``view: True`` in
the manifest. Environments can be configured without views using
``view: False``. For backwards compatibility reasons, environments
with no ``view`` key are treated the same as ``view: True``.
From the command line, the ``spack env create`` command takes an
argument ``--with-view [PATH]`` that sets the path for a single, default
view. If no path is specified, the default path is used (``view:
true``). The argument ``--without-view`` can be used to create an
True``). The argument ``--without-view`` can be used to create an
environment without any view configured.
The ``spack env view`` command can be used to change the manage views
@@ -1122,18 +1119,11 @@ the projection under ``all`` before reaching those entries.
Activating environment views
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``spack env activate <env>`` has two effects:
1. It activates the environment so that further Spack commands such
as ``spack install`` will run in the context of the environment.
2. It activates the view so that environment variables such as
``PATH`` are updated to include the view.
Without further arguments, the ``default`` view of the environment is
activated. If a view with a different name has to be activated,
``spack env activate --with-view <name> <env>`` can be
used instead. You can also activate the environment without modifying
further environment variables using ``--without-view``.
The ``spack env activate`` command will put the default view for the
environment into the user's path, in addition to activating the
environment for Spack commands. The arguments ``-v,--with-view`` and
``-V,--without-view`` can be used to tune this behavior. The default
behavior is to activate with the environment view if there is one.
The environment variables affected by the ``spack env activate``
command and the paths that are used to update them are determined by
@@ -1156,8 +1146,8 @@ relevant variable if the path exists. For this reason, it is not
recommended to use non-default projections with the default view of an
environment.
The ``spack env deactivate`` command will remove the active view of
the Spack environment from the user's environment variables.
The ``spack env deactivate`` command will remove the default view of
the environment from the user's path.
.. _env-generate-depfile:
@@ -1316,7 +1306,7 @@ index once every package is pushed. Note how this target uses the generated
example/push/%: example/install/%
@mkdir -p $(dir $@)
$(info About to push $(SPEC) to a buildcache)
$(SPACK) -e . buildcache push --only=package $(BUILDCACHE_DIR) /$(HASH)
$(SPACK) -e . buildcache push --allow-root --only=package $(BUILDCACHE_DIR) /$(HASH)
@touch $@
push: $(addprefix example/push/,$(example/SPACK_PACKAGE_IDS))

View File

@@ -1,4 +1,4 @@
sphinx==7.4.7
sphinx==7.2.6
sphinxcontrib-programoutput==0.17
sphinx_design==0.6.0
sphinx-rtd-theme==2.0.0
@@ -6,8 +6,8 @@ python-levenshtein==0.25.1
docutils==0.20.1
pygments==2.18.0
urllib3==2.2.2
pytest==8.3.1
pytest==8.2.2
isort==5.13.2
black==24.4.2
flake8==7.1.0
mypy==1.11.0
mypy==1.10.1

View File

@@ -129,10 +129,10 @@ def _bootstrap_config_scopes() -> Sequence["spack.config.ConfigScope"]:
configuration_paths = (spack.config.CONFIGURATION_DEFAULTS_PATH, ("bootstrap", _config_path()))
for name, path in configuration_paths:
platform = spack.platforms.host().name
platform_scope = spack.config.DirectoryConfigScope(
f"{name}/{platform}", os.path.join(path, platform)
platform_scope = spack.config.ConfigScope(
"/".join([name, platform]), os.path.join(path, platform)
)
generic_scope = spack.config.DirectoryConfigScope(name, path)
generic_scope = spack.config.ConfigScope(name, path)
config_scopes.extend([generic_scope, platform_scope])
msg = "[BOOTSTRAP CONFIG SCOPE] name={0}, path={1}"
tty.debug(msg.format(generic_scope.name, generic_scope.path))

View File

@@ -1473,7 +1473,7 @@ def long_message(self):
out.write(" {0}\n".format(self.log_name))
# Also output the test log path IF it exists
if self.context != "test" and have_log:
if self.context != "test":
test_log = join_path(os.path.dirname(self.log_name), spack_install_test_log)
if os.path.isfile(test_log):
out.write("\nSee test log for details:\n")

View File

@@ -124,8 +124,6 @@ def cuda_flags(arch_list):
# minimum supported versions
conflicts("%gcc@:4", when="+cuda ^cuda@11.0:")
conflicts("%gcc@:5", when="+cuda ^cuda@11.4:")
conflicts("%gcc@:7.2", when="+cuda ^cuda@12.4:")
conflicts("%clang@:6", when="+cuda ^cuda@12.2:")
# maximum supported version
# NOTE:
@@ -213,16 +211,12 @@ def cuda_flags(arch_list):
conflicts("%intel@19.0:", when="+cuda ^cuda@:10.0")
conflicts("%intel@19.1:", when="+cuda ^cuda@:10.1")
conflicts("%intel@19.2:", when="+cuda ^cuda@:11.1.0")
conflicts("%intel@2021:", when="+cuda ^cuda@:11.4.0")
# XL is mostly relevant for ppc64le Linux
conflicts("%xl@:12,14:", when="+cuda ^cuda@:9.1")
conflicts("%xl@:12,14:15,17:", when="+cuda ^cuda@9.2")
conflicts("%xl@:12,17:", when="+cuda ^cuda@:11.1.0")
# PowerPC.
conflicts("target=ppc64le", when="+cuda ^cuda@12.5:")
# Darwin.
# TODO: add missing conflicts for %apple-clang cuda@:10
conflicts("platform=darwin", when="+cuda ^cuda@11.0.2:")
conflicts("platform=darwin", when="+cuda ^cuda@11.0.2: ")

View File

@@ -72,7 +72,7 @@ def build_directory(self):
def build_args(self):
"""Arguments for ``go build``."""
# Pass ldflags -s = --strip-all and -w = --no-warnings by default
return ["-modcacherw", "-ldflags", "-s -w", "-o", f"{self.pkg.name}"]
return ["-ldflags", "-s -w", "-o", f"{self.pkg.name}"]
@property
def check_args(self):

View File

@@ -34,8 +34,6 @@ def _misc_cache():
return spack.util.file_cache.FileCache(path)
FileCacheType = Union[spack.util.file_cache.FileCache, llnl.util.lang.Singleton]
#: Spack's cache for small data
MISC_CACHE: Union[spack.util.file_cache.FileCache, llnl.util.lang.Singleton] = (
llnl.util.lang.Singleton(_misc_cache)

View File

@@ -809,8 +809,7 @@ def ensure_expected_target_path(path):
cli_scopes = [
os.path.relpath(s.path, concrete_env_dir)
for s in cfg.scopes().values()
if not s.writable
and isinstance(s, (cfg.DirectoryConfigScope))
if isinstance(s, cfg.ImmutableConfigScope)
and s.path not in env_includes
and os.path.exists(s.path)
]

View File

@@ -336,7 +336,6 @@ def display_specs(specs, args=None, **kwargs):
groups (bool): display specs grouped by arch/compiler (default True)
decorator (typing.Callable): function to call to decorate specs
all_headers (bool): show headers even when arch/compiler aren't defined
status_fn (typing.Callable): if provided, prepend install-status info
output (typing.IO): A file object to write to. Default is ``sys.stdout``
"""
@@ -360,7 +359,6 @@ def get_arg(name, default=None):
groups = get_arg("groups", True)
all_headers = get_arg("all_headers", False)
output = get_arg("output", sys.stdout)
status_fn = get_arg("status_fn", None)
decorator = get_arg("decorator", None)
if decorator is None:
@@ -388,13 +386,6 @@ def get_arg(name, default=None):
def fmt(s, depth=0):
"""Formatter function for all output specs"""
string = ""
if status_fn:
# This was copied from spec.tree's colorization logic
# then shortened because it seems like status_fn should
# always return an InstallStatus
string += colorize(status_fn(s).value)
if hashes:
string += gray_hash(s, hlen) + " "
string += depth * " "

View File

@@ -165,7 +165,7 @@ def _reset(args):
if not ok_to_continue:
raise RuntimeError("Aborting")
for scope in spack.config.CONFIG.writable_scopes:
for scope in spack.config.CONFIG.file_scopes:
# The default scope should stay untouched
if scope.name == "defaults":
continue

View File

@@ -70,6 +70,12 @@ def setup_parser(subparser: argparse.ArgumentParser):
push = subparsers.add_parser("push", aliases=["create"], help=push_fn.__doc__)
push.add_argument("-f", "--force", action="store_true", help="overwrite tarball if it exists")
push.add_argument(
"--allow-root",
"-a",
action="store_true",
help="allow install root string in binary files after RPATH substitution",
)
push_sign = push.add_mutually_exclusive_group(required=False)
push_sign.add_argument(
"--unsigned",
@@ -184,6 +190,10 @@ def setup_parser(subparser: argparse.ArgumentParser):
keys.add_argument("-f", "--force", action="store_true", help="force new download of keys")
keys.set_defaults(func=keys_fn)
preview = subparsers.add_parser("preview", help=preview_fn.__doc__)
arguments.add_common_arguments(preview, ["installed_specs"])
preview.set_defaults(func=preview_fn)
# Check if binaries need to be rebuilt on remote mirror
check = subparsers.add_parser("check", help=check_fn.__doc__)
check.add_argument(
@@ -394,6 +404,11 @@ def push_fn(args):
else:
roots = spack.cmd.require_active_env(cmd_name="buildcache push").concrete_roots()
if args.allow_root:
tty.warn(
"The flag `--allow-root` is the default in Spack 0.21, will be removed in Spack 0.22"
)
mirror: spack.mirror.Mirror = args.mirror
# Check if this is an OCI image.
@@ -945,6 +960,14 @@ def keys_fn(args):
bindist.get_keys(args.install, args.trust, args.force)
def preview_fn(args):
"""analyze an installed spec and reports whether executables and libraries are relocatable"""
tty.warn(
"`spack buildcache preview` is deprecated since `spack buildcache push --allow-root` is "
"now the default. This command will be removed in Spack 0.22"
)
def check_fn(args: argparse.Namespace):
"""check specs against remote binary mirror(s) to see if any need to be rebuilt

View File

@@ -156,7 +156,7 @@ def print_flattened_configuration(*, blame: bool) -> None:
"""
env = ev.active_environment()
if env is not None:
pristine = env.manifest.yaml_content
pristine = env.manifest.pristine_yaml_content
flattened = pristine.copy()
flattened[spack.schema.env.TOP_LEVEL_KEY] = pristine[spack.schema.env.TOP_LEVEL_KEY].copy()
else:
@@ -264,9 +264,7 @@ def config_remove(args):
def _can_update_config_file(scope: spack.config.ConfigScope, cfg_file):
if isinstance(scope, spack.config.SingleFileScope):
return fs.can_access(cfg_file)
elif isinstance(scope, spack.config.DirectoryConfigScope):
return fs.can_write_to_dir(scope.path) and fs.can_access(cfg_file)
return False
return fs.can_write_to_dir(scope.path) and fs.can_access(cfg_file)
def _config_change_requires_scope(path, spec, scope, match_spec=None):
@@ -364,11 +362,14 @@ def config_change(args):
def config_update(args):
# Read the configuration files
spack.config.CONFIG.get_config(args.section, scope=args.scope)
updates: List[spack.config.ConfigScope] = [
x
for x in spack.config.CONFIG.format_updates[args.section]
if not isinstance(x, spack.config.InternalConfigScope) and x.writable
]
updates: List[spack.config.ConfigScope] = list(
filter(
lambda s: not isinstance(
s, (spack.config.InternalConfigScope, spack.config.ImmutableConfigScope)
),
spack.config.CONFIG.format_updates[args.section],
)
)
cannot_overwrite, skip_system_scope = [], False
for scope in updates:
@@ -446,7 +447,7 @@ def _can_revert_update(scope_dir, cfg_file, bkp_file):
def config_revert(args):
scopes = [args.scope] if args.scope else [x.name for x in spack.config.CONFIG.writable_scopes]
scopes = [args.scope] if args.scope else [x.name for x in spack.config.CONFIG.file_scopes]
# Search for backup files in the configuration scopes
Entry = collections.namedtuple("Entry", ["scope", "cfg", "bkp"])

View File

@@ -2,6 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import re
import sys
@@ -933,7 +934,7 @@ def get_repository(args, name):
# Figure out where the new package should live
repo_path = args.repo
if repo_path is not None:
repo = spack.repo.from_path(repo_path)
repo = spack.repo.Repo(repo_path)
if spec.namespace and spec.namespace != repo.namespace:
tty.die(
"Can't create package with namespace {0} in repo with "
@@ -941,7 +942,9 @@ def get_repository(args, name):
)
else:
if spec.namespace:
repo = spack.repo.PATH.get_repo(spec.namespace)
repo = spack.repo.PATH.get_repo(spec.namespace, None)
if not repo:
tty.die("Unknown namespace: '{0}'".format(spec.namespace))
else:
repo = spack.repo.PATH.first_repo()

View File

@@ -47,6 +47,16 @@ def inverted_dependencies():
dependents of, e.g., `mpi`, but virtuals are not included as
actual dependents.
"""
dag = {}
for pkg_cls in spack.repo.PATH.all_package_classes():
dag.setdefault(pkg_cls.name, set())
for dep in pkg_cls.dependencies_by_name():
deps = [dep]
# expand virtuals if necessary
if spack.repo.PATH.is_virtual(dep):
deps += [s.name for s in spack.repo.PATH.providers_for(dep)]
dag = collections.defaultdict(set)
for pkg_cls in spack.repo.PATH.all_package_classes():
for _, deps_by_name in pkg_cls.dependencies.items():

View File

@@ -123,7 +123,7 @@ def edit(parser, args):
spack.util.editor.editor(*paths)
elif names:
if args.repo:
repo = spack.repo.from_path(args.repo)
repo = spack.repo.Repo(args.repo)
elif args.namespace:
repo = spack.repo.PATH.get_repo(args.namespace)
else:

View File

@@ -7,7 +7,7 @@
import os
import re
import sys
from typing import List, Optional, Set
from typing import List, Optional
import llnl.util.tty as tty
import llnl.util.tty.colify as colify
@@ -19,7 +19,6 @@
import spack.detection
import spack.error
import spack.repo
import spack.spec
import spack.util.environment
from spack.cmd.common import arguments
@@ -139,26 +138,14 @@ def external_find(args):
candidate_packages, path_hints=args.path, max_workers=args.jobs
)
new_specs = spack.detection.update_configuration(
new_entries = spack.detection.update_configuration(
detected_packages, scope=args.scope, buildable=not args.not_buildable
)
# If the user runs `spack external find --not-buildable mpich` we also mark `mpi` non-buildable
# to avoid that the concretizer picks a different mpi provider.
if new_specs and args.not_buildable:
virtuals: Set[str] = {
virtual.name
for new_spec in new_specs
for virtual_specs in spack.repo.PATH.get_pkg_class(new_spec.name).provided.values()
for virtual in virtual_specs
}
new_virtuals = spack.detection.set_virtuals_nonbuildable(virtuals, scope=args.scope)
new_specs.extend(spack.spec.Spec(name) for name in new_virtuals)
if new_specs:
if new_entries:
path = spack.config.CONFIG.get_config_filename(args.scope, "packages")
tty.msg(f"The following specs have been detected on this system and added to {path}")
spack.cmd.display_specs(new_specs)
msg = "The following specs have been detected on this system and added to {0}"
tty.msg(msg.format(path))
spack.cmd.display_specs(new_entries)
else:
tty.msg("No new external packages detected")

View File

@@ -46,10 +46,6 @@ def setup_parser(subparser):
help="output specs as machine-readable json records",
)
subparser.add_argument(
"-I", "--install-status", action="store_true", help="show install status of packages"
)
subparser.add_argument(
"-d", "--deps", action="store_true", help="output dependencies along with found specs"
)
@@ -297,24 +293,25 @@ def root_decorator(spec, string):
)
print()
if args.show_concretized:
tty.msg("Concretized roots")
cmd.display_specs(env.specs_by_hash.values(), args, decorator=decorator)
print()
# Display a header for the installed packages section IF there are installed
# packages. If there aren't any, we'll just end up printing "0 installed packages"
# later.
if results and not args.only_roots:
tty.msg("Installed packages")
def find(parser, args):
env = ev.active_environment()
q_args = query_arguments(args)
results = args.specs(**q_args)
env = ev.active_environment()
if not env and args.only_roots:
tty.die("-r / --only-roots requires an active environment")
if not env and args.show_concretized:
tty.die("-c / --show-concretized requires an active environment")
if env:
if args.constraint:
init_specs = spack.cmd.parse_specs(args.constraint)
results = env.all_matching_specs(*init_specs)
else:
results = env.all_specs()
else:
q_args = query_arguments(args)
results = args.specs(**q_args)
decorator = make_env_decorator(env) if env else lambda s, f: f
@@ -335,11 +332,6 @@ def find(parser, args):
if args.loaded:
results = spack.cmd.filter_loaded_specs(results)
if args.install_status or args.show_concretized:
status_fn = spack.spec.Spec.install_status
else:
status_fn = None
# Display the result
if args.json:
cmd.display_specs_as_json(results, deps=args.deps)
@@ -348,34 +340,12 @@ def find(parser, args):
if env:
display_env(env, args, decorator, results)
count_suffix = " (not shown)"
if not args.only_roots:
display_results = results
if not args.show_concretized:
display_results = list(x for x in results if x.installed)
cmd.display_specs(
display_results, args, decorator=decorator, all_headers=True, status_fn=status_fn
)
cmd.display_specs(results, args, decorator=decorator, all_headers=True)
count_suffix = ""
# print number of installed packages last (as the list may be long)
if sys.stdout.isatty() and args.groups:
installed_suffix = ""
concretized_suffix = " to be installed"
if args.only_roots:
installed_suffix += " (not shown)"
concretized_suffix += " (not shown)"
else:
if env and not args.show_concretized:
concretized_suffix += " (show with `spack find -c`)"
pkg_type = "loaded" if args.loaded else "installed"
spack.cmd.print_how_many_pkgs(
list(x for x in results if x.installed), pkg_type, suffix=installed_suffix
)
if env:
spack.cmd.print_how_many_pkgs(
list(x for x in results if not x.installed),
"concretized",
suffix=concretized_suffix,
)
spack.cmd.print_how_many_pkgs(results, pkg_type, suffix=count_suffix)

View File

@@ -56,6 +56,7 @@ def roots_from_environments(args, active_env):
# -e says "also preserve things needed by this particular env"
for env_name_or_dir in args.except_environment:
print("HMM", env_name_or_dir)
if ev.exists(env_name_or_dir):
env = ev.read(env_name_or_dir)
elif ev.is_env_dir(env_name_or_dir):

View File

@@ -169,9 +169,7 @@ def pkg_hash(args):
def get_grep(required=False):
"""Get a grep command to use with ``spack pkg grep``."""
grep = exe.which(os.environ.get("SPACK_GREP") or "grep", required=required)
grep.ignore_quotes = True # allow `spack pkg grep '"quoted string"'` without warning
return grep
return exe.which(os.environ.get("SPACK_GREP") or "grep", required=required)
def pkg_grep(args, unknown_args):

View File

@@ -91,7 +91,7 @@ def repo_add(args):
tty.die("Not a Spack repository: %s" % path)
# Make sure it's actually a spack repository by constructing it.
repo = spack.repo.from_path(canon_path)
repo = spack.repo.Repo(canon_path)
# If that succeeds, finally add it to the configuration.
repos = spack.config.get("repos", scope=args.scope)
@@ -124,7 +124,7 @@ def repo_remove(args):
# If it is a namespace, remove corresponding repo
for path in repos:
try:
repo = spack.repo.from_path(path)
repo = spack.repo.Repo(path)
if repo.namespace == namespace_or_path:
repos.remove(path)
spack.config.set("repos", repos, args.scope)
@@ -142,7 +142,7 @@ def repo_list(args):
repos = []
for r in roots:
try:
repos.append(spack.repo.from_path(r))
repos.append(spack.repo.Repo(r))
except spack.repo.RepoError:
continue

View File

@@ -18,6 +18,7 @@
import llnl.util.tty as tty
from llnl.util.filesystem import path_contains_subdirectory, paths_containing_libs
import spack.compilers
import spack.error
import spack.schema.environment
import spack.spec

View File

@@ -260,7 +260,7 @@ def _init_compiler_config(
def compiler_config_files():
config_files = list()
config = spack.config.CONFIG
for scope in config.writable_scopes:
for scope in config.file_scopes:
name = scope.name
compiler_config = config.get("compilers", scope=name)
if compiler_config:
@@ -488,7 +488,7 @@ def supported_compilers_for_host_platform() -> List[str]:
return supported_compilers_for_platform(host_plat)
def supported_compilers_for_platform(platform: "spack.platforms.Platform") -> List[str]:
def supported_compilers_for_platform(platform: spack.platforms.Platform) -> List[str]:
"""Return a set of compiler class objects supported by Spack
that are also supported by the provided platform

View File

@@ -4,7 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
from os.path import dirname, join
from os.path import dirname
from llnl.util import tty
@@ -135,12 +135,8 @@ def setup_custom_environment(self, pkg, env):
# It is located in the same directory as the driver. Error message:
# clang++: error: unable to execute command:
# Executable "sycl-post-link" doesn't exist!
# also ensures that shared objects and libraries required by the compiler,
# e.g. libonnx, can be found succesfully
# due to a fix, this is no longer required for OneAPI versions >= 2024.2
if self.cxx and pkg.spec.satisfies("%oneapi@:2024.1"):
if self.cxx:
env.prepend_path("PATH", dirname(self.cxx))
env.prepend_path("LD_LIBRARY_PATH", join(dirname(dirname(self.cxx)), "lib"))
# 2024 release bumped the libsycl version because of an ABI
# change, 2024 compilers are required. You will see this

View File

@@ -35,10 +35,11 @@
import os
import re
import sys
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Union
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Type, Union
from llnl.util import filesystem, lang, tty
import spack.compilers
import spack.paths
import spack.platforms
import spack.schema
@@ -116,39 +117,21 @@
class ConfigScope:
def __init__(self, name: str) -> None:
self.name = name
self.writable = False
self.sections = syaml.syaml_dict()
"""This class represents a configuration scope.
def get_section_filename(self, section: str) -> str:
raise NotImplementedError
A scope is one directory containing named configuration files.
Each file is a config "section" (e.g., mirrors, compilers, etc.).
"""
def get_section(self, section: str) -> Optional[YamlConfigDict]:
raise NotImplementedError
def _write_section(self, section: str) -> None:
raise NotImplementedError
def __init__(self, name, path) -> None:
self.name = name # scope name.
self.path = path # path to directory containing configs.
self.sections = syaml.syaml_dict() # sections read from config files.
@property
def is_platform_dependent(self) -> bool:
return False
def clear(self) -> None:
"""Empty cached config information."""
self.sections = syaml.syaml_dict()
def __repr__(self) -> str:
return f"<ConfigScope: {self.name}>"
class DirectoryConfigScope(ConfigScope):
"""Config scope backed by a directory containing one file per section."""
def __init__(self, name: str, path: str, *, writable: bool = True) -> None:
super().__init__(name)
self.path = path
self.writable = writable
"""Returns true if the scope name is platform specific"""
return os.sep in self.name
def get_section_filename(self, section: str) -> str:
"""Returns the filename associated with a given section"""
@@ -165,15 +148,14 @@ def get_section(self, section: str) -> Optional[YamlConfigDict]:
return self.sections[section]
def _write_section(self, section: str) -> None:
if not self.writable:
raise ConfigError(f"Cannot write to immutable scope {self}")
filename = self.get_section_filename(section)
data = self.get_section(section)
if data is None:
return
validate(data, SECTION_SCHEMAS[section])
# We copy data here to avoid adding defaults at write time
validate_data = copy.deepcopy(data)
validate(validate_data, SECTION_SCHEMAS[section])
try:
filesystem.mkdirp(self.path)
@@ -182,23 +164,19 @@ def _write_section(self, section: str) -> None:
except (syaml.SpackYAMLError, OSError) as e:
raise ConfigFileError(f"cannot write to '{filename}'") from e
@property
def is_platform_dependent(self) -> bool:
"""Returns true if the scope name is platform specific"""
return "/" in self.name
def clear(self) -> None:
"""Empty cached config information."""
self.sections = syaml.syaml_dict()
def __repr__(self) -> str:
return f"<ConfigScope: {self.name}: {self.path}>"
class SingleFileScope(ConfigScope):
"""This class represents a configuration scope in a single YAML file."""
def __init__(
self,
name: str,
path: str,
schema: YamlConfigDict,
*,
yaml_path: Optional[List[str]] = None,
writable: bool = True,
self, name: str, path: str, schema: YamlConfigDict, yaml_path: Optional[List[str]] = None
) -> None:
"""Similar to ``ConfigScope`` but can be embedded in another schema.
@@ -217,13 +195,15 @@ def __init__(
config:
install_tree: $spack/opt/spack
"""
super().__init__(name)
super().__init__(name, path)
self._raw_data: Optional[YamlConfigDict] = None
self.schema = schema
self.path = path
self.writable = writable
self.yaml_path = yaml_path or []
@property
def is_platform_dependent(self) -> bool:
return False
def get_section_filename(self, section) -> str:
return self.path
@@ -277,8 +257,6 @@ def get_section(self, section: str) -> Optional[YamlConfigDict]:
return self.sections.get(section, None)
def _write_section(self, section: str) -> None:
if not self.writable:
raise ConfigError(f"Cannot write to immutable scope {self}")
data_to_write: Optional[YamlConfigDict] = self._raw_data
# If there is no existing data, this section SingleFileScope has never
@@ -323,6 +301,19 @@ def __repr__(self) -> str:
return f"<SingleFileScope: {self.name}: {self.path}>"
class ImmutableConfigScope(ConfigScope):
"""A configuration scope that cannot be written to.
This is used for ConfigScopes passed on the command line.
"""
def _write_section(self, section) -> None:
raise ConfigError(f"Cannot write to immutable scope {self}")
def __repr__(self) -> str:
return f"<ImmutableConfigScope: {self.name}: {self.path}>"
class InternalConfigScope(ConfigScope):
"""An internal configuration scope that is not persisted to a file.
@@ -332,7 +323,7 @@ class InternalConfigScope(ConfigScope):
"""
def __init__(self, name: str, data: Optional[YamlConfigDict] = None) -> None:
super().__init__(name)
super().__init__(name, None)
self.sections = syaml.syaml_dict()
if data is not None:
@@ -342,6 +333,9 @@ def __init__(self, name: str, data: Optional[YamlConfigDict] = None) -> None:
validate({section: dsec}, SECTION_SCHEMAS[section])
self.sections[section] = _mark_internal(syaml.syaml_dict({section: dsec}), name)
def get_section_filename(self, section: str) -> str:
raise NotImplementedError("Cannot get filename for InternalConfigScope.")
def get_section(self, section: str) -> Optional[YamlConfigDict]:
"""Just reads from an internal dictionary."""
if section not in self.sections:
@@ -446,21 +440,27 @@ def remove_scope(self, scope_name: str) -> Optional[ConfigScope]:
return scope
@property
def writable_scopes(self) -> Generator[ConfigScope, None, None]:
"""Generator of writable scopes with an associated file."""
return (s for s in self.scopes.values() if s.writable)
def file_scopes(self) -> List[ConfigScope]:
"""List of writable scopes with an associated file."""
return [
s
for s in self.scopes.values()
if (type(s) is ConfigScope or type(s) is SingleFileScope)
]
def highest_precedence_scope(self) -> ConfigScope:
"""Writable scope with highest precedence."""
return next(s for s in reversed(self.scopes.values()) if s.writable) # type: ignore
"""Non-internal scope with highest precedence."""
return next(reversed(self.file_scopes))
def highest_precedence_non_platform_scope(self) -> ConfigScope:
"""Writable non-platform scope with highest precedence"""
return next(
s
for s in reversed(self.scopes.values()) # type: ignore
if s.writable and not s.is_platform_dependent
)
"""Non-internal non-platform scope with highest precedence
Platform-specific scopes are of the form scope/platform"""
generator = reversed(self.file_scopes)
highest = next(generator)
while highest and highest.is_platform_dependent:
highest = next(generator)
return highest
def matching_scopes(self, reg_expr) -> List[ConfigScope]:
"""
@@ -755,14 +755,13 @@ def override(
def _add_platform_scope(
cfg: Union[Configuration, lang.Singleton], name: str, path: str, writable: bool = True
cfg: Union[Configuration, lang.Singleton], scope_type: Type[ConfigScope], name: str, path: str
) -> None:
"""Add a platform-specific subdirectory for the current platform."""
platform = spack.platforms.host().name
scope = DirectoryConfigScope(
f"{name}/{platform}", os.path.join(path, platform), writable=writable
)
cfg.push_scope(scope)
plat_name = os.path.join(name, platform)
plat_path = os.path.join(path, platform)
cfg.push_scope(scope_type(plat_name, plat_path))
def config_paths_from_entry_points() -> List[Tuple[str, str]]:
@@ -793,27 +792,22 @@ def config_paths_from_entry_points() -> List[Tuple[str, str]]:
def _add_command_line_scopes(
cfg: Union[Configuration, lang.Singleton], command_line_scopes: List[str]
) -> None:
"""Add additional scopes from the --config-scope argument, either envs or dirs."""
import spack.environment.environment as env # circular import
"""Add additional scopes from the --config-scope argument.
Command line scopes are named after their position in the arg list.
"""
for i, path in enumerate(command_line_scopes):
name = f"cmd_scope_{i}"
# We ensure that these scopes exist and are readable, as they are
# provided on the command line by the user.
if not os.path.isdir(path):
raise ConfigError(f"config scope is not a directory: '{path}'")
elif not os.access(path, os.R_OK):
raise ConfigError(f"config scope is not readable: '{path}'")
if env.exists(path): # managed environment
manifest = env.EnvironmentManifestFile(env.root(path))
elif env.is_env_dir(path): # anonymous environment
manifest = env.EnvironmentManifestFile(path)
elif os.path.isdir(path): # directory with config files
cfg.push_scope(DirectoryConfigScope(name, path, writable=False))
_add_platform_scope(cfg, name, path, writable=False)
continue
else:
raise ConfigError(f"Invalid configuration scope: {path}")
for scope in manifest.env_config_scopes:
scope.name = f"{name}:{scope.name}"
scope.writable = False
cfg.push_scope(scope)
# name based on order on the command line
name = f"cmd_scope_{i:d}"
cfg.push_scope(ImmutableConfigScope(name, path))
_add_platform_scope(cfg, ImmutableConfigScope, name, path)
def create() -> Configuration:
@@ -857,10 +851,10 @@ def create() -> Configuration:
# add each scope and its platform-specific directory
for name, path in configuration_paths:
cfg.push_scope(DirectoryConfigScope(name, path))
cfg.push_scope(ConfigScope(name, path))
# Each scope can have per-platfom overrides in subdirectories
_add_platform_scope(cfg, name, path)
_add_platform_scope(cfg, ConfigScope, name, path)
# add command-line scopes
_add_command_line_scopes(cfg, COMMAND_LINE_SCOPES)
@@ -975,7 +969,7 @@ def set(path: str, value: Any, scope: Optional[str] = None) -> None:
def add_default_platform_scope(platform: str) -> None:
plat_name = os.path.join("defaults", platform)
plat_path = os.path.join(CONFIGURATION_DEFAULTS_PATH[1], platform)
CONFIG.push_scope(DirectoryConfigScope(plat_name, plat_path))
CONFIG.push_scope(ConfigScope(plat_name, plat_path))
def scopes() -> Dict[str, ConfigScope]:
@@ -984,10 +978,19 @@ def scopes() -> Dict[str, ConfigScope]:
def writable_scopes() -> List[ConfigScope]:
"""Return list of writable scopes. Higher-priority scopes come first in the list."""
scopes = [x for x in CONFIG.scopes.values() if x.writable]
scopes.reverse()
return scopes
"""
Return list of writable scopes. Higher-priority scopes come first in the
list.
"""
return list(
reversed(
list(
x
for x in CONFIG.scopes.values()
if not isinstance(x, (InternalConfigScope, ImmutableConfigScope))
)
)
)
def writable_scope_names() -> List[str]:
@@ -1077,8 +1080,11 @@ def validate(
"""
import jsonschema
# Validate a copy to avoid adding defaults
# This allows us to round-trip data without adding to it.
test_data = syaml.deepcopy(data)
try:
spack.schema.Validator(schema).validate(data)
spack.schema.Validator(schema).validate(test_data)
except jsonschema.ValidationError as e:
if hasattr(e.instance, "lc"):
line_number = e.instance.lc.line + 1
@@ -1087,7 +1093,7 @@ def validate(
raise ConfigFormatError(e, data, filename, line_number) from e
# return the validated data so that we can access the raw data
# mostly relevant for environments
return data
return test_data
def read_config_file(
@@ -1593,7 +1599,7 @@ def _config_from(scopes_or_paths: List[Union[ConfigScope, str]]) -> Configuratio
path = os.path.normpath(scope_or_path)
assert os.path.isdir(path), f'"{path}" must be a directory'
name = os.path.basename(path)
scopes.append(DirectoryConfigScope(name, path))
scopes.append(ConfigScope(name, path))
configuration = Configuration(*scopes)
return configuration

View File

@@ -2,12 +2,7 @@
# 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 DetectedPackage, executable_prefix, update_configuration
from .path import by_path, executables_in_path
from .test import detection_tests
@@ -17,6 +12,5 @@
"executables_in_path",
"executable_prefix",
"update_configuration",
"set_virtuals_nonbuildable",
"detection_tests",
]

View File

@@ -252,27 +252,6 @@ def update_configuration(
return all_new_specs
def set_virtuals_nonbuildable(virtuals: Set[str], scope: Optional[str] = None) -> List[str]:
"""Update packages:virtual:buildable:False for the provided virtual packages, if the property
is not set by the user. Returns the list of virtual packages that have been updated."""
packages = spack.config.get("packages")
new_config = {}
for virtual in virtuals:
# If the user has set the buildable prop do not override it
if virtual in packages and "buildable" in packages[virtual]:
continue
new_config[virtual] = {"buildable": False}
# Update the provided scope
spack.config.set(
"packages",
spack.config.merge_yaml(spack.config.get("packages", scope=scope), new_config),
scope=scope,
)
return list(new_config.keys())
def _windows_drive() -> str:
"""Return Windows drive string extracted from the PROGRAMFILES environment variable,
which is guaranteed to be defined for all logins.

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, List, Optional, Set, Tuple
import llnl.util.filesystem
import llnl.util.lang
@@ -200,7 +200,7 @@ class Finder:
def default_path_hints(self) -> List[str]:
return []
def search_patterns(self, *, pkg: Type["spack.package_base.PackageBase"]) -> List[str]:
def search_patterns(self, *, pkg: "spack.package_base.PackageBase") -> List[str]:
"""Returns the list of patterns used to match candidate files.
Args:
@@ -226,7 +226,7 @@ def prefix_from_path(self, *, path: str) -> str:
raise NotImplementedError("must be implemented by derived classes")
def detect_specs(
self, *, pkg: Type["spack.package_base.PackageBase"], paths: List[str]
self, *, pkg: "spack.package_base.PackageBase", paths: List[str]
) -> List[DetectedPackage]:
"""Given a list of files matching the search patterns, returns a list of detected specs.
@@ -327,7 +327,7 @@ class ExecutablesFinder(Finder):
def default_path_hints(self) -> List[str]:
return spack.util.environment.get_path("PATH")
def search_patterns(self, *, pkg: Type["spack.package_base.PackageBase"]) -> List[str]:
def search_patterns(self, *, pkg: "spack.package_base.PackageBase") -> List[str]:
result = []
if hasattr(pkg, "executables") and hasattr(pkg, "platform_executables"):
result = pkg.platform_executables()
@@ -356,7 +356,7 @@ class LibrariesFinder(Finder):
DYLD_LIBRARY_PATH, DYLD_FALLBACK_LIBRARY_PATH, and standard system library paths
"""
def search_patterns(self, *, pkg: Type["spack.package_base.PackageBase"]) -> List[str]:
def search_patterns(self, *, pkg: "spack.package_base.PackageBase") -> List[str]:
result = []
if hasattr(pkg, "libraries"):
result = pkg.libraries

View File

@@ -90,14 +90,14 @@ class OpenMpi(Package):
_patch_order_index = 0
SpecType = str
SpecType = Union["spack.spec.Spec", str]
DepType = Union[Tuple[str, ...], str]
WhenType = Optional[Union["spack.spec.Spec", str, bool]]
Patcher = Callable[[Union["spack.package_base.PackageBase", Dependency]], None]
PatchesType = Optional[Union[Patcher, str, List[Union[Patcher, str]]]]
SUPPORTED_LANGUAGES = ("fortran", "cxx", "c")
SUPPORTED_LANGUAGES = ("fortran", "cxx")
def _make_when_spec(value: WhenType) -> Optional["spack.spec.Spec"]:
@@ -475,7 +475,7 @@ def _execute_version(pkg, ver, **kwargs):
def _depends_on(
pkg: "spack.package_base.PackageBase",
spec: "spack.spec.Spec",
spec: SpecType,
*,
when: WhenType = None,
type: DepType = dt.DEFAULT_TYPES,
@@ -485,10 +485,11 @@ def _depends_on(
if not when_spec:
return
if not spec.name:
raise DependencyError(f"Invalid dependency specification in package '{pkg.name}':", spec)
if pkg.name == spec.name:
raise CircularReferenceError(f"Package '{pkg.name}' cannot depend on itself.")
dep_spec = spack.spec.Spec(spec)
if not dep_spec.name:
raise DependencyError("Invalid dependency specification in package '%s':" % pkg.name, spec)
if pkg.name == dep_spec.name:
raise CircularReferenceError("Package '%s' cannot depend on itself." % pkg.name)
depflag = dt.canonicalize(type)
@@ -504,7 +505,7 @@ def _depends_on(
# ensure `Spec.virtual` is a valid thing to call in a directive.
# For now, we comment out the following check to allow for virtual packages
# with package files.
# if patches and spec.virtual:
# if patches and dep_spec.virtual:
# raise DependencyPatchError("Cannot patch a virtual dependency.")
# ensure patches is a list
@@ -519,13 +520,13 @@ def _depends_on(
# this is where we actually add the dependency to this package
deps_by_name = pkg.dependencies.setdefault(when_spec, {})
dependency = deps_by_name.get(spec.name)
dependency = deps_by_name.get(dep_spec.name)
if not dependency:
dependency = Dependency(pkg, spec, depflag=depflag)
deps_by_name[spec.name] = dependency
dependency = Dependency(pkg, dep_spec, depflag=depflag)
deps_by_name[dep_spec.name] = dependency
else:
dependency.spec.constrain(spec, deps=False)
dependency.spec.constrain(dep_spec, deps=False)
dependency.depflag |= depflag
# apply patches to the dependency
@@ -590,13 +591,12 @@ def depends_on(
@see The section "Dependency specs" in the Spack Packaging Guide.
"""
dep_spec = spack.spec.Spec(spec)
if dep_spec.name in SUPPORTED_LANGUAGES:
if spack.spec.Spec(spec).name in SUPPORTED_LANGUAGES:
assert type == "build", "languages must be of 'build' type"
return _language(lang_spec_str=spec, when=when)
def _execute_depends_on(pkg: "spack.package_base.PackageBase"):
_depends_on(pkg, dep_spec, when=when, type=type, patches=patches)
_depends_on(pkg, spec, when=when, type=type, patches=patches)
return _execute_depends_on
@@ -666,24 +666,25 @@ def extends(spec, when=None, type=("build", "run"), patches=None):
keyword arguments can be passed to extends() so that extension
packages can pass parameters to the extendee's extension
mechanism."""
mechanism.
"""
def _execute_extends(pkg):
when_spec = _make_when_spec(when)
if not when_spec:
return
dep_spec = spack.spec.Spec(spec)
_depends_on(pkg, dep_spec, when=when, type=type, patches=patches)
_depends_on(pkg, spec, when=when, type=type, patches=patches)
spec_obj = spack.spec.Spec(spec)
# When extending python, also add a dependency on python-venv. This is done so that
# Spack environment views are Python virtual environments.
if dep_spec.name == "python" and not pkg.name == "python-venv":
_depends_on(pkg, spack.spec.Spec("python-venv"), when=when, type=("build", "run"))
if spec_obj.name == "python" and not pkg.name == "python-venv":
_depends_on(pkg, "python-venv", when=when, type=("build", "run"))
# TODO: the values of the extendees dictionary are not used. Remove in next refactor.
pkg.extendees[dep_spec.name] = (dep_spec, None)
pkg.extendees[spec_obj.name] = (spec_obj, None)
return _execute_extends

View File

@@ -5,7 +5,7 @@
import collections
import collections.abc
import contextlib
import errno
import copy
import os
import pathlib
import re
@@ -24,7 +24,6 @@
from llnl.util.link_tree import ConflictingSpecsError
from llnl.util.symlink import readlink, symlink
import spack.caches
import spack.cmd
import spack.compilers
import spack.concretize
@@ -269,7 +268,9 @@ def root(name):
def exists(name):
"""Whether an environment with this name exists or not."""
return valid_env_name(name) and os.path.isdir(_root(name))
if not valid_env_name(name):
return False
return os.path.isdir(root(name))
def active(name):
@@ -528,8 +529,8 @@ def _read_yaml(str_or_file):
)
filename = getattr(str_or_file, "name", None)
spack.config.validate(data, spack.schema.env.schema, filename)
return data
default_data = spack.config.validate(data, spack.schema.env.schema, filename)
return data, default_data
def _write_yaml(data, str_or_file):
@@ -789,23 +790,6 @@ def regenerate(self, concrete_roots: List[Spec]) -> None:
root_dirname = os.path.dirname(self.root)
tmp_symlink_name = os.path.join(root_dirname, "._view_link")
# Remove self.root if is it an empty dir, since we need a symlink there. Note that rmdir
# fails if self.root is a symlink.
try:
os.rmdir(self.root)
except (FileNotFoundError, NotADirectoryError):
pass
except OSError as e:
if e.errno == errno.ENOTEMPTY:
msg = "it is a non-empty directory"
elif e.errno == errno.EACCES:
msg = "of insufficient permissions"
else:
raise
raise SpackEnvironmentViewError(
f"The environment view in {self.root} cannot not be created because {msg}."
) from e
# Create a new view
try:
fs.mkdirp(new_root)
@@ -937,7 +921,7 @@ def __init__(self, manifest_dir: Union[str, pathlib.Path]) -> None:
def _load_manifest_file(self):
"""Instantiate and load the manifest file contents into memory."""
with lk.ReadTransaction(self.txlock):
self.manifest = EnvironmentManifestFile(self.path, self.name)
self.manifest = EnvironmentManifestFile(self.path)
with self.manifest.use_config():
self._read()
@@ -974,25 +958,18 @@ def write_transaction(self):
"""Get a write lock context manager for use in a `with` block."""
return lk.WriteTransaction(self.txlock, acquire=self._re_read)
def _process_definition(self, entry):
def _process_definition(self, item):
"""Process a single spec definition item."""
when_string = entry.get("when")
if when_string is not None:
when = _eval_conditional(when_string)
assert len([x for x in entry if x != "when"]) == 1
else:
when = True
assert len(entry) == 1
entry = copy.deepcopy(item)
when = _eval_conditional(entry.pop("when", "True"))
assert len(entry) == 1
if when:
for name, spec_list in entry.items():
if name == "when":
continue
user_specs = SpecList(name, spec_list, self.spec_lists.copy())
if name in self.spec_lists:
self.spec_lists[name].extend(user_specs)
else:
self.spec_lists[name] = user_specs
name, spec_list = next(iter(entry.items()))
user_specs = SpecList(name, spec_list, self.spec_lists.copy())
if name in self.spec_lists:
self.spec_lists[name].extend(user_specs)
else:
self.spec_lists[name] = user_specs
def _process_view(self, env_view: Optional[Union[bool, str, Dict]]):
"""Process view option(s), which can be boolean, string, or None.
@@ -2565,7 +2542,7 @@ def _concretize_task(packed_arguments) -> Tuple[int, Spec, float]:
def make_repo_path(root):
"""Make a RepoPath from the repo subdirectories in an environment."""
path = spack.repo.RepoPath(cache=spack.caches.MISC_CACHE)
path = spack.repo.RepoPath()
if os.path.isdir(root):
for repo_root in os.listdir(root):
@@ -2574,7 +2551,7 @@ def make_repo_path(root):
if not os.path.isdir(repo_root):
continue
repo = spack.repo.from_path(repo_root)
repo = spack.repo.Repo(repo_root)
path.put_last(repo)
return path
@@ -2775,11 +2752,10 @@ def from_lockfile(manifest_dir: Union[pathlib.Path, str]) -> "EnvironmentManifes
manifest.flush()
return manifest
def __init__(self, manifest_dir: Union[pathlib.Path, str], name: Optional[str] = None) -> None:
def __init__(self, manifest_dir: Union[pathlib.Path, str]) -> None:
self.manifest_dir = pathlib.Path(manifest_dir)
self.name = name or str(manifest_dir)
self.manifest_file = self.manifest_dir / manifest_name
self.scope_name = f"env:{self.name}"
self.scope_name = f"env:{environment_name(self.manifest_dir)}"
self.config_stage_dir = os.path.join(env_subdir_path(manifest_dir), "config")
#: Configuration scopes associated with this environment. Note that these are not
@@ -2791,8 +2767,12 @@ def __init__(self, manifest_dir: Union[pathlib.Path, str], name: Optional[str] =
raise SpackEnvironmentError(msg)
with self.manifest_file.open() as f:
self.yaml_content = _read_yaml(f)
raw, with_defaults_added = _read_yaml(f)
#: Pristine YAML content, without defaults being added
self.pristine_yaml_content = raw
#: YAML content with defaults added by Spack, if they're missing
self.yaml_content = with_defaults_added
self.changed = False
def _all_matches(self, user_spec: str) -> List[str]:
@@ -2806,7 +2786,7 @@ def _all_matches(self, user_spec: str) -> List[str]:
ValueError: if no equivalent match is found
"""
result = []
for yaml_spec_str in self.configuration["specs"]:
for yaml_spec_str in self.pristine_configuration["specs"]:
if Spec(yaml_spec_str) == Spec(user_spec):
result.append(yaml_spec_str)
@@ -2821,6 +2801,7 @@ def add_user_spec(self, user_spec: str) -> None:
Args:
user_spec: user spec to be appended
"""
self.pristine_configuration.setdefault("specs", []).append(user_spec)
self.configuration.setdefault("specs", []).append(user_spec)
self.changed = True
@@ -2835,6 +2816,7 @@ def remove_user_spec(self, user_spec: str) -> None:
"""
try:
for key in self._all_matches(user_spec):
self.pristine_configuration["specs"].remove(key)
self.configuration["specs"].remove(key)
except ValueError as e:
msg = f"cannot remove {user_spec} from {self}, no such spec exists"
@@ -2852,6 +2834,7 @@ def override_user_spec(self, user_spec: str, idx: int) -> None:
SpackEnvironmentError: when the user spec cannot be overridden
"""
try:
self.pristine_configuration["specs"][idx] = user_spec
self.configuration["specs"][idx] = user_spec
except ValueError as e:
msg = f"cannot override {user_spec} from {self}"
@@ -2864,10 +2847,10 @@ def set_include_concrete(self, include_concrete: List[str]) -> None:
Args:
include_concrete: list of already existing concrete environments to include
"""
self.configuration[included_concrete_name] = []
self.pristine_configuration[included_concrete_name] = []
for env_path in include_concrete:
self.configuration[included_concrete_name].append(env_path)
self.pristine_configuration[included_concrete_name].append(env_path)
self.changed = True
@@ -2881,13 +2864,14 @@ def add_definition(self, user_spec: str, list_name: str) -> None:
Raises:
SpackEnvironmentError: is no valid definition exists already
"""
defs = self.configuration.get("definitions", [])
defs = self.pristine_configuration.get("definitions", [])
msg = f"cannot add {user_spec} to the '{list_name}' definition, no valid list exists"
for idx, item in self._iterate_on_definitions(defs, list_name=list_name, err_msg=msg):
item[list_name].append(user_spec)
break
self.configuration["definitions"][idx][list_name].append(user_spec)
self.changed = True
def remove_definition(self, user_spec: str, list_name: str) -> None:
@@ -2901,7 +2885,7 @@ def remove_definition(self, user_spec: str, list_name: str) -> None:
SpackEnvironmentError: if the user spec cannot be removed from the list,
or the list does not exist
"""
defs = self.configuration.get("definitions", [])
defs = self.pristine_configuration.get("definitions", [])
msg = (
f"cannot remove {user_spec} from the '{list_name}' definition, "
f"no valid list exists"
@@ -2914,6 +2898,7 @@ def remove_definition(self, user_spec: str, list_name: str) -> None:
except ValueError:
pass
self.configuration["definitions"][idx][list_name].remove(user_spec)
self.changed = True
def override_definition(self, user_spec: str, *, override: str, list_name: str) -> None:
@@ -2928,7 +2913,7 @@ def override_definition(self, user_spec: str, *, override: str, list_name: str)
Raises:
SpackEnvironmentError: if the user spec cannot be overridden
"""
defs = self.configuration.get("definitions", [])
defs = self.pristine_configuration.get("definitions", [])
msg = f"cannot override {user_spec} with {override} in the '{list_name}' definition"
for idx, item in self._iterate_on_definitions(defs, list_name=list_name, err_msg=msg):
@@ -2939,6 +2924,7 @@ def override_definition(self, user_spec: str, *, override: str, list_name: str)
except ValueError:
pass
self.configuration["definitions"][idx][list_name][sub_index] = override
self.changed = True
def _iterate_on_definitions(self, definitions, *, list_name, err_msg):
@@ -2970,6 +2956,7 @@ def set_default_view(self, view: Union[bool, str, pathlib.Path, Dict[str, str]])
True the default view is used for the environment, if False there's no view.
"""
if isinstance(view, dict):
self.pristine_configuration["view"][default_view_name].update(view)
self.configuration["view"][default_view_name].update(view)
self.changed = True
return
@@ -2977,13 +2964,15 @@ def set_default_view(self, view: Union[bool, str, pathlib.Path, Dict[str, str]])
if not isinstance(view, bool):
view = str(view)
self.pristine_configuration["view"] = view
self.configuration["view"] = view
self.changed = True
def remove_default_view(self) -> None:
"""Removes the default view from the manifest file"""
view_data = self.configuration.get("view")
view_data = self.pristine_configuration.get("view")
if isinstance(view_data, collections.abc.Mapping):
self.pristine_configuration["view"].pop(default_view_name)
self.configuration["view"].pop(default_view_name)
self.changed = True
return
@@ -2996,12 +2985,17 @@ def flush(self) -> None:
return
with fs.write_tmp_and_move(os.path.realpath(self.manifest_file)) as f:
_write_yaml(self.yaml_content, f)
_write_yaml(self.pristine_yaml_content, f)
self.changed = False
@property
def configuration(self):
def pristine_configuration(self):
"""Return the dictionaries in the pristine YAML, without the top level attribute"""
return self.pristine_yaml_content[TOP_LEVEL_KEY]
@property
def configuration(self):
"""Return the dictionaries in the YAML, without the top level attribute"""
return self.yaml_content[TOP_LEVEL_KEY]
def __len__(self):
@@ -3033,11 +3027,12 @@ def included_config_scopes(self) -> List[spack.config.ConfigScope]:
SpackEnvironmentError: if the manifest includes a remote file but
no configuration stage directory has been identified
"""
scopes: List[spack.config.ConfigScope] = []
scopes = []
# load config scopes added via 'include:', in reverse so that
# highest-precedence scopes are last.
includes = self[TOP_LEVEL_KEY].get("include", [])
env_name = environment_name(self.manifest_dir)
missing = []
for i, config_path in enumerate(reversed(includes)):
# allow paths to contain spack config/environment variables, etc.
@@ -3100,22 +3095,24 @@ def included_config_scopes(self) -> List[spack.config.ConfigScope]:
if os.path.isdir(config_path):
# directories are treated as regular ConfigScopes
config_name = f"env:{self.name}:{os.path.basename(config_path)}"
tty.debug(f"Creating DirectoryConfigScope {config_name} for '{config_path}'")
scopes.append(spack.config.DirectoryConfigScope(config_name, config_path))
config_name = "env:%s:%s" % (env_name, os.path.basename(config_path))
tty.debug("Creating ConfigScope {0} for '{1}'".format(config_name, config_path))
scope = spack.config.ConfigScope(config_name, config_path)
elif os.path.exists(config_path):
# files are assumed to be SingleFileScopes
config_name = f"env:{self.name}:{config_path}"
tty.debug(f"Creating SingleFileScope {config_name} for '{config_path}'")
scopes.append(
spack.config.SingleFileScope(
config_name, config_path, spack.schema.merged.schema
)
config_name = "env:%s:%s" % (env_name, config_path)
tty.debug(
"Creating SingleFileScope {0} for '{1}'".format(config_name, config_path)
)
scope = spack.config.SingleFileScope(
config_name, config_path, spack.schema.merged.schema
)
else:
missing.append(config_path)
continue
scopes.append(scope)
if missing:
msg = "Detected {0} missing include path(s):".format(len(missing))
msg += "\n {0}".format("\n ".join(missing))
@@ -3132,10 +3129,7 @@ def env_config_scopes(self) -> List[spack.config.ConfigScope]:
scopes: List[spack.config.ConfigScope] = [
*self.included_config_scopes,
spack.config.SingleFileScope(
self.scope_name,
str(self.manifest_file),
spack.schema.env.schema,
yaml_path=[TOP_LEVEL_KEY],
self.scope_name, str(self.manifest_file), spack.schema.env.schema, [TOP_LEVEL_KEY]
),
]
ensure_no_disallowed_env_config_mods(scopes)

View File

@@ -582,7 +582,7 @@ def dump_packages(spec: "spack.spec.Spec", path: str) -> None:
# Create a source repo and get the pkg directory out of it.
try:
source_repo = spack.repo.from_path(source_repo_root)
source_repo = spack.repo.Repo(source_repo_root)
source_pkg_dir = source_repo.dirname_for_package_name(node.name)
except spack.repo.RepoError as err:
tty.debug(f"Failed to create source repo for {node.name}: {str(err)}")
@@ -593,7 +593,7 @@ def dump_packages(spec: "spack.spec.Spec", path: str) -> None:
dest_repo_root = os.path.join(path, node.namespace)
if not os.path.exists(dest_repo_root):
spack.repo.create_repo(dest_repo_root)
repo = spack.repo.from_path(dest_repo_root)
repo = spack.repo.Repo(dest_repo_root)
# Get the location of the package in the dest repo.
dest_pkg_dir = repo.dirname_for_package_name(node.name)

View File

@@ -444,9 +444,8 @@ def make_argument_parser(**kwargs):
"--config-scope",
dest="config_scopes",
action="append",
metavar="DIR|ENV",
help="add directory or environment as read-only configuration scope, without activating "
"the environment.",
metavar="DIR",
help="add a custom configuration scope",
)
parser.add_argument(
"-d",

View File

@@ -35,7 +35,6 @@
import spack.compilers
import spack.config
import spack.dependency
import spack.deptypes as dt
import spack.directives
import spack.directory_layout
@@ -200,10 +199,10 @@ def __init__(cls, name, bases, attr_dict):
# assumed to be detectable
if hasattr(cls, "executables") or hasattr(cls, "libraries"):
# Append a tag to each detectable package, so that finding them is faster
if not hasattr(cls, "tags"):
if hasattr(cls, "tags"):
getattr(cls, "tags").append(DetectablePackageMeta.TAG)
else:
setattr(cls, "tags", [DetectablePackageMeta.TAG])
elif DetectablePackageMeta.TAG not in cls.tags:
cls.tags.append(DetectablePackageMeta.TAG)
@classmethod
def platform_executables(cls):
@@ -749,6 +748,11 @@ def __init__(self, spec):
self._fetch_time = 0.0
self.win_rpath = fsys.WindowsSimulatedRPath(self)
if self.is_extension:
pkg_cls = spack.repo.PATH.get_pkg_class(self.extendee_spec.name)
pkg_cls(self.extendee_spec)._check_extendable()
super().__init__()
@classmethod
@@ -2384,6 +2388,10 @@ def do_deprecate(self, deprecator, link_fn):
PackageBase.uninstall_by_spec(spec, force=True, deprecator=deprecator)
link_fn(deprecator.prefix, spec.prefix)
def _check_extendable(self):
if not self.extendable:
raise ValueError("Package %s is not extendable!" % self.name)
def view(self):
"""Create a view with the prefix of this package as the root.
Extensions added to this view will modify the installation prefix of

View File

@@ -9,7 +9,7 @@
import os.path
import pathlib
import sys
from typing import Any, Dict, Optional, Tuple, Type, Union
from typing import Any, Dict, Optional, Tuple, Type
import llnl.util.filesystem
from llnl.url import allowed_archive
@@ -65,9 +65,6 @@ def apply_patch(
patch(*args)
PatchPackageType = Union["spack.package_base.PackageBase", Type["spack.package_base.PackageBase"]]
class Patch:
"""Base class for patches.
@@ -80,7 +77,7 @@ class Patch:
def __init__(
self,
pkg: PatchPackageType,
pkg: "spack.package_base.PackageBase",
path_or_url: str,
level: int,
working_dir: str,
@@ -162,7 +159,7 @@ class FilePatch(Patch):
def __init__(
self,
pkg: PatchPackageType,
pkg: "spack.package_base.PackageBase",
relative_path: str,
level: int,
working_dir: str,
@@ -186,7 +183,7 @@ def __init__(
abs_path: Optional[str] = None
# At different times we call FilePatch on instances and classes
pkg_cls = pkg if inspect.isclass(pkg) else pkg.__class__
for cls in inspect.getmro(pkg_cls): # type: ignore
for cls in inspect.getmro(pkg_cls):
if not hasattr(cls, "module"):
# We've gone too far up the MRO
break
@@ -245,7 +242,7 @@ class UrlPatch(Patch):
def __init__(
self,
pkg: PatchPackageType,
pkg: "spack.package_base.PackageBase",
url: str,
level: int = 1,
*,
@@ -364,9 +361,8 @@ def from_dict(
"""
repository = repository or spack.repo.PATH
owner = dictionary.get("owner")
if owner is None:
raise ValueError(f"Invalid patch dictionary: {dictionary}")
assert isinstance(owner, str)
if "owner" not in dictionary:
raise ValueError("Invalid patch dictionary: %s" % dictionary)
pkg_cls = repository.get_pkg_class(owner)
if "url" in dictionary:

View File

@@ -25,8 +25,7 @@
import traceback
import types
import uuid
import warnings
from typing import Any, Dict, Generator, List, Optional, Set, Tuple, Type, Union
from typing import Any, Dict, List, Set, Tuple, Union
import llnl.path
import llnl.util.filesystem as fs
@@ -127,35 +126,11 @@ def exec_module(self, module):
class ReposFinder:
"""MetaPathFinder class that loads a Python module corresponding to a Spack package.
"""MetaPathFinder class that loads a Python module corresponding to a Spack package
Returns a loader based on the inspection of the current repository list.
Return a loader based on the inspection of the current global repository list.
"""
def __init__(self):
self._repo_init = _path
self._repo = None
@property
def current_repository(self):
if self._repo is None:
self._repo = self._repo_init()
return self._repo
@current_repository.setter
def current_repository(self, value):
self._repo = value
@contextlib.contextmanager
def switch_repo(self, substitute: "RepoType"):
"""Switch the current repository list for the duration of the context manager."""
old = self.current_repository
try:
self.current_repository = substitute
yield
finally:
self.current_repository = old
def find_spec(self, fullname, python_path, target=None):
# "target" is not None only when calling importlib.reload()
if target is not None:
@@ -174,14 +149,9 @@ def compute_loader(self, fullname):
# namespaces are added to repo, and package modules are leaves.
namespace, dot, module_name = fullname.rpartition(".")
# If it's a module in some repo, or if it is the repo's namespace, let the repo handle it.
is_repo_path = isinstance(self.current_repository, RepoPath)
if is_repo_path:
repos = self.current_repository.repos
else:
repos = [self.current_repository]
for repo in repos:
# If it's a module in some repo, or if it is the repo's
# namespace, let the repo handle it.
for repo in PATH.repos:
# We are using the namespace of the repo and the repo contains the package
if namespace == repo.full_namespace:
# With 2 nested conditionals we can call "repo.real_name" only once
@@ -195,7 +165,7 @@ def compute_loader(self, fullname):
# No repo provides the namespace, but it is a valid prefix of
# something in the RepoPath.
if is_repo_path and self.current_repository.by_namespace.is_prefix(fullname):
if PATH.by_namespace.is_prefix(fullname):
return SpackNamespaceLoader()
return None
@@ -590,7 +560,7 @@ def __init__(
self,
package_checker: FastPackageChecker,
namespace: str,
cache: "spack.caches.FileCacheType",
cache: spack.util.file_cache.FileCache,
):
self.checker = package_checker
self.packages_path = self.checker.packages_path
@@ -675,39 +645,33 @@ class RepoPath:
repository.
Args:
repos: list Repo objects or paths to put in this RepoPath
cache: file cache associated with this repository
overrides: dict mapping package name to class attribute overrides for that package
repos (list): list Repo objects or paths to put in this RepoPath
"""
def __init__(
self,
*repos: Union[str, "Repo"],
cache: "spack.caches.FileCacheType",
overrides: Optional[Dict[str, Any]] = None,
) -> None:
self.repos: List[Repo] = []
def __init__(self, *repos, **kwargs):
cache = kwargs.get("cache", spack.caches.MISC_CACHE)
self.repos = []
self.by_namespace = nm.NamespaceTrie()
self._provider_index: Optional[spack.provider_index.ProviderIndex] = None
self._patch_index: Optional[spack.patch.PatchCache] = None
self._tag_index: Optional[spack.tag.TagIndex] = None
self._provider_index = None
self._patch_index = None
self._tag_index = None
# Add each repo to this path.
for repo in repos:
try:
if isinstance(repo, str):
repo = Repo(repo, cache=cache, overrides=overrides)
repo.finder(self)
repo = Repo(repo, cache=cache)
self.put_last(repo)
except RepoError as e:
tty.warn(
f"Failed to initialize repository: '{repo}'.",
"Failed to initialize repository: '%s'." % repo,
e.message,
"To remove the bad repository, run this command:",
f" spack repo rm {repo}",
" spack repo rm %s" % repo,
)
def put_first(self, repo: "Repo") -> None:
def put_first(self, repo):
"""Add repo first in the search path."""
if isinstance(repo, RepoPath):
for r in reversed(repo.repos):
@@ -735,34 +699,50 @@ def remove(self, repo):
if repo in self.repos:
self.repos.remove(repo)
def get_repo(self, namespace: str) -> "Repo":
"""Get a repository by namespace."""
def get_repo(self, namespace, default=NOT_PROVIDED):
"""Get a repository by namespace.
Arguments:
namespace:
Look up this namespace in the RepoPath, and return it if found.
Optional Arguments:
default:
If default is provided, return it when the namespace
isn't found. If not, raise an UnknownNamespaceError.
"""
full_namespace = python_package_for_repo(namespace)
if full_namespace not in self.by_namespace:
raise UnknownNamespaceError(namespace)
if default == NOT_PROVIDED:
raise UnknownNamespaceError(namespace)
return default
return self.by_namespace[full_namespace]
def first_repo(self) -> Optional["Repo"]:
def first_repo(self):
"""Get the first repo in precedence order."""
return self.repos[0] if self.repos else None
@llnl.util.lang.memoized
def _all_package_names_set(self, include_virtuals) -> Set[str]:
def _all_package_names_set(self, include_virtuals):
return {name for repo in self.repos for name in repo.all_package_names(include_virtuals)}
@llnl.util.lang.memoized
def _all_package_names(self, include_virtuals: bool) -> List[str]:
def _all_package_names(self, include_virtuals):
"""Return all unique package names in all repositories."""
return sorted(self._all_package_names_set(include_virtuals), key=lambda n: n.lower())
def all_package_names(self, include_virtuals: bool = False) -> List[str]:
def all_package_names(self, include_virtuals=False):
return self._all_package_names(include_virtuals)
def package_path(self, name: str) -> str:
def package_path(self, name):
"""Get path to package.py file for this repo."""
return self.repo_for_pkg(name).package_path(name)
def all_package_paths(self) -> Generator[str, None, None]:
def all_package_paths(self):
for name in self.all_package_names():
yield self.package_path(name)
@@ -778,52 +758,53 @@ def packages_with_tags(self, *tags: str, full: bool = False) -> Set[str]:
for pkg in repo.packages_with_tags(*tags)
}
def all_package_classes(self) -> Generator[Type["spack.package_base.PackageBase"], None, None]:
def all_package_classes(self):
for name in self.all_package_names():
yield self.get_pkg_class(name)
@property
def provider_index(self) -> spack.provider_index.ProviderIndex:
def provider_index(self):
"""Merged ProviderIndex from all Repos in the RepoPath."""
if self._provider_index is None:
self._provider_index = spack.provider_index.ProviderIndex(repository=self)
for repo in reversed(self.repos):
self._provider_index.merge(repo.provider_index)
return self._provider_index
@property
def tag_index(self) -> spack.tag.TagIndex:
def tag_index(self):
"""Merged TagIndex from all Repos in the RepoPath."""
if self._tag_index is None:
self._tag_index = spack.tag.TagIndex(repository=self)
for repo in reversed(self.repos):
self._tag_index.merge(repo.tag_index)
return self._tag_index
@property
def patch_index(self) -> spack.patch.PatchCache:
def patch_index(self):
"""Merged PatchIndex from all Repos in the RepoPath."""
if self._patch_index is None:
self._patch_index = spack.patch.PatchCache(repository=self)
for repo in reversed(self.repos):
self._patch_index.update(repo.patch_index)
return self._patch_index
@autospec
def providers_for(self, virtual_spec: "spack.spec.Spec") -> List["spack.spec.Spec"]:
def providers_for(self, vpkg_spec):
providers = [
spec
for spec in self.provider_index.providers_for(virtual_spec)
for spec in self.provider_index.providers_for(vpkg_spec)
if spec.name in self._all_package_names_set(include_virtuals=False)
]
if not providers:
raise UnknownPackageError(virtual_spec.fullname)
raise UnknownPackageError(vpkg_spec.fullname)
return providers
@autospec
def extensions_for(
self, extendee_spec: "spack.spec.Spec"
) -> List["spack.package_base.PackageBase"]:
def extensions_for(self, extendee_spec):
return [
pkg_cls(spack.spec.Spec(pkg_cls.name))
for pkg_cls in self.all_package_classes()
@@ -834,7 +815,7 @@ def last_mtime(self):
"""Time a package file in this repo was last updated."""
return max(repo.last_mtime() for repo in self.repos)
def repo_for_pkg(self, spec: Union[str, "spack.spec.Spec"]) -> "Repo":
def repo_for_pkg(self, spec):
"""Given a spec, get the repository for its package."""
# We don't @_autospec this function b/c it's called very frequently
# and we want to avoid parsing str's into Specs unnecessarily.
@@ -859,20 +840,17 @@ def repo_for_pkg(self, spec: Union[str, "spack.spec.Spec"]) -> "Repo":
return repo
# If the package isn't in any repo, return the one with
# highest precedence. This is for commands like `spack edit`
# highest precedence. This is for commands like `spack edit`
# that can operate on packages that don't exist yet.
selected = self.first_repo()
if selected is None:
raise UnknownPackageError(name)
return selected
return self.first_repo()
def get(self, spec: "spack.spec.Spec") -> "spack.package_base.PackageBase":
def get(self, spec):
"""Returns the package associated with the supplied spec."""
msg = "RepoPath.get can only be called on concrete specs"
assert isinstance(spec, spack.spec.Spec) and spec.concrete, msg
return self.repo_for_pkg(spec).get(spec)
def get_pkg_class(self, pkg_name: str) -> Type["spack.package_base.PackageBase"]:
def get_pkg_class(self, pkg_name):
"""Find a class for the spec's package and return the class object."""
return self.repo_for_pkg(pkg_name).get_pkg_class(pkg_name)
@@ -885,26 +863,26 @@ def dump_provenance(self, spec, path):
"""
return self.repo_for_pkg(spec).dump_provenance(spec, path)
def dirname_for_package_name(self, pkg_name: str) -> str:
def dirname_for_package_name(self, pkg_name):
return self.repo_for_pkg(pkg_name).dirname_for_package_name(pkg_name)
def filename_for_package_name(self, pkg_name: str) -> str:
def filename_for_package_name(self, pkg_name):
return self.repo_for_pkg(pkg_name).filename_for_package_name(pkg_name)
def exists(self, pkg_name: str) -> bool:
def exists(self, pkg_name):
"""Whether package with the give name exists in the path's repos.
Note that virtual packages do not "exist".
"""
return any(repo.exists(pkg_name) for repo in self.repos)
def _have_name(self, pkg_name: str) -> bool:
def _have_name(self, pkg_name):
have_name = pkg_name is not None
if have_name and not isinstance(pkg_name, str):
raise ValueError(f"is_virtual(): expected package name, got {type(pkg_name)}")
raise ValueError("is_virtual(): expected package name, got %s" % type(pkg_name))
return have_name
def is_virtual(self, pkg_name: str) -> bool:
def is_virtual(self, pkg_name):
"""Return True if the package with this name is virtual, False otherwise.
This function use the provider index. If calling from a code block that
@@ -916,7 +894,7 @@ def is_virtual(self, pkg_name: str) -> bool:
have_name = self._have_name(pkg_name)
return have_name and pkg_name in self.provider_index
def is_virtual_safe(self, pkg_name: str) -> bool:
def is_virtual_safe(self, pkg_name):
"""Return True if the package with this name is virtual, False otherwise.
This function doesn't use the provider index.
@@ -937,28 +915,18 @@ class Repo:
Each package repository must have a top-level configuration file
called `repo.yaml`.
Currently, `repo.yaml` must define:
Currently, `repo.yaml` this must define:
`namespace`:
A Python namespace where the repository's packages should live.
`subdirectory`:
An optional subdirectory name where packages are placed
"""
def __init__(
self,
root: str,
*,
cache: "spack.caches.FileCacheType",
overrides: Optional[Dict[str, Any]] = None,
) -> None:
def __init__(self, root, cache=None):
"""Instantiate a package repository from a filesystem path.
Args:
root: the root directory of the repository
cache: file cache associated with this repository
overrides: dict mapping package name to class attribute overrides for that package
"""
# Root directory, containing _repo.yaml and package dirs
# Allow roots to by spack-relative by starting with '$spack'
@@ -971,20 +939,20 @@ def check(condition, msg):
# Validate repository layout.
self.config_file = os.path.join(self.root, repo_config_name)
check(os.path.isfile(self.config_file), f"No {repo_config_name} found in '{root}'")
check(os.path.isfile(self.config_file), "No %s found in '%s'" % (repo_config_name, root))
# Read configuration and validate namespace
config = self._read_config()
check(
"namespace" in config,
f"{os.path.join(root, repo_config_name)} must define a namespace.",
"%s must define a namespace." % os.path.join(root, repo_config_name),
)
self.namespace = config["namespace"]
check(
re.match(r"[a-zA-Z][a-zA-Z0-9_.]+", self.namespace),
f"Invalid namespace '{self.namespace}' in repo '{self.root}'. "
"Namespaces must be valid python identifiers separated by '.'",
("Invalid namespace '%s' in repo '%s'. " % (self.namespace, self.root))
+ "Namespaces must be valid python identifiers separated by '.'",
)
# Set up 'full_namespace' to include the super-namespace
@@ -996,26 +964,23 @@ def check(condition, msg):
packages_dir = config.get("subdirectory", packages_dir_name)
self.packages_path = os.path.join(self.root, packages_dir)
check(
os.path.isdir(self.packages_path), f"No directory '{packages_dir}' found in '{root}'"
os.path.isdir(self.packages_path),
"No directory '%s' found in '%s'" % (packages_dir, root),
)
# Class attribute overrides by package name
self.overrides = overrides or {}
# Optional reference to a RepoPath to influence module import from spack.pkg
self._finder: Optional[RepoPath] = None
# These are internal cache variables.
self._modules = {}
self._classes = {}
self._instances = {}
# Maps that goes from package name to corresponding file stat
self._fast_package_checker: Optional[FastPackageChecker] = None
self._fast_package_checker = None
# Indexes for this repository, computed lazily
self._repo_index: Optional[RepoIndex] = None
self._cache = cache
self._repo_index = None
self._cache = cache or spack.caches.MISC_CACHE
def finder(self, value: RepoPath) -> None:
self._finder = value
def real_name(self, import_name: str) -> Optional[str]:
def real_name(self, import_name):
"""Allow users to import Spack packages using Python identifiers.
A python identifier might map to many different Spack package
@@ -1034,21 +999,18 @@ def real_name(self, import_name: str) -> Optional[str]:
return import_name
options = nm.possible_spack_module_names(import_name)
try:
options.remove(import_name)
except ValueError:
pass
options.remove(import_name)
for name in options:
if name in self:
return name
return None
def is_prefix(self, fullname: str) -> bool:
def is_prefix(self, fullname):
"""True if fullname is a prefix of this Repo's namespace."""
parts = fullname.split(".")
return self._names[: len(parts)] == parts
def _read_config(self) -> Dict[str, str]:
def _read_config(self):
"""Check for a YAML config file in this db's root directory."""
try:
with open(self.config_file) as reponame_file:
@@ -1059,14 +1021,14 @@ def _read_config(self) -> Dict[str, str]:
or "repo" not in yaml_data
or not isinstance(yaml_data["repo"], dict)
):
tty.die(f"Invalid {repo_config_name} in repository {self.root}")
tty.die("Invalid %s in repository %s" % (repo_config_name, self.root))
return yaml_data["repo"]
except IOError:
tty.die(f"Error reading {self.config_file} when opening {self.root}")
tty.die("Error reading %s when opening %s" % (self.config_file, self.root))
def get(self, spec: "spack.spec.Spec") -> "spack.package_base.PackageBase":
def get(self, spec):
"""Returns the package associated with the supplied spec."""
msg = "Repo.get can only be called on concrete specs"
assert isinstance(spec, spack.spec.Spec) and spec.concrete, msg
@@ -1087,13 +1049,16 @@ def get(self, spec: "spack.spec.Spec") -> "spack.package_base.PackageBase":
# pass these through as their error messages will be fine.
raise
except Exception as e:
tty.debug(e)
# Make sure other errors in constructors hit the error
# handler by wrapping them
tty.debug(e)
raise FailedConstructorError(spec.fullname, *sys.exc_info()) from e
if spack.config.get("config:debug"):
sys.excepthook(*sys.exc_info())
raise FailedConstructorError(spec.fullname, *sys.exc_info())
@autospec
def dump_provenance(self, spec: "spack.spec.Spec", path: str) -> None:
def dump_provenance(self, spec, path):
"""Dump provenance information for a spec to a particular path.
This dumps the package file and any associated patch files.
@@ -1101,7 +1066,7 @@ def dump_provenance(self, spec: "spack.spec.Spec", path: str) -> None:
"""
if spec.namespace and spec.namespace != self.namespace:
raise UnknownPackageError(
f"Repository {self.namespace} does not contain package {spec.fullname}."
"Repository %s does not contain package %s." % (self.namespace, spec.fullname)
)
package_path = self.filename_for_package_name(spec.name)
@@ -1118,13 +1083,17 @@ def dump_provenance(self, spec: "spack.spec.Spec", path: str) -> None:
if os.path.exists(patch.path):
fs.install(patch.path, path)
else:
warnings.warn(f"Patch file did not exist: {patch.path}")
tty.warn("Patch file did not exist: %s" % patch.path)
# Install the package.py file itself.
fs.install(self.filename_for_package_name(spec.name), path)
def purge(self):
"""Clear entire package instance cache."""
self._instances.clear()
@property
def index(self) -> RepoIndex:
def index(self):
"""Construct the index for this repo lazily."""
if self._repo_index is None:
self._repo_index = RepoIndex(self._pkg_checker, self.namespace, cache=self._cache)
@@ -1134,40 +1103,42 @@ def index(self) -> RepoIndex:
return self._repo_index
@property
def provider_index(self) -> spack.provider_index.ProviderIndex:
def provider_index(self):
"""A provider index with names *specific* to this repo."""
return self.index["providers"]
@property
def tag_index(self) -> spack.tag.TagIndex:
def tag_index(self):
"""Index of tags and which packages they're defined on."""
return self.index["tags"]
@property
def patch_index(self) -> spack.patch.PatchCache:
def patch_index(self):
"""Index of patches and packages they're defined on."""
return self.index["patches"]
@autospec
def providers_for(self, vpkg_spec: "spack.spec.Spec") -> List["spack.spec.Spec"]:
def providers_for(self, vpkg_spec):
providers = self.provider_index.providers_for(vpkg_spec)
if not providers:
raise UnknownPackageError(vpkg_spec.fullname)
return providers
@autospec
def extensions_for(
self, extendee_spec: "spack.spec.Spec"
) -> List["spack.package_base.PackageBase"]:
result = [pkg_cls(spack.spec.Spec(pkg_cls.name)) for pkg_cls in self.all_package_classes()]
return [x for x in result if x.extends(extendee_spec)]
def extensions_for(self, extendee_spec):
return [
pkg_cls(spack.spec.Spec(pkg_cls.name))
for pkg_cls in self.all_package_classes()
if pkg_cls(spack.spec.Spec(pkg_cls.name)).extends(extendee_spec)
]
def dirname_for_package_name(self, pkg_name: str) -> str:
"""Given a package name, get the directory containing its package.py file."""
def dirname_for_package_name(self, pkg_name):
"""Get the directory name for a particular package. This is the
directory that contains its package.py file."""
_, unqualified_name = self.partition_package_name(pkg_name)
return os.path.join(self.packages_path, unqualified_name)
def filename_for_package_name(self, pkg_name: str) -> str:
def filename_for_package_name(self, pkg_name):
"""Get the filename for the module we should load for a particular
package. Packages for a Repo live in
``$root/<package_name>/package.py``
@@ -1180,23 +1151,23 @@ def filename_for_package_name(self, pkg_name: str) -> str:
return os.path.join(pkg_dir, package_file_name)
@property
def _pkg_checker(self) -> FastPackageChecker:
def _pkg_checker(self):
if self._fast_package_checker is None:
self._fast_package_checker = FastPackageChecker(self.packages_path)
return self._fast_package_checker
def all_package_names(self, include_virtuals: bool = False) -> List[str]:
def all_package_names(self, include_virtuals=False):
"""Returns a sorted list of all package names in the Repo."""
names = sorted(self._pkg_checker.keys())
if include_virtuals:
return names
return [x for x in names if not self.is_virtual(x)]
def package_path(self, name: str) -> str:
def package_path(self, name):
"""Get path to package.py file for this repo."""
return os.path.join(self.packages_path, name, package_file_name)
def all_package_paths(self) -> Generator[str, None, None]:
def all_package_paths(self):
for name in self.all_package_names():
yield self.package_path(name)
@@ -1205,7 +1176,7 @@ def packages_with_tags(self, *tags: str) -> Set[str]:
v.intersection_update(*(self.tag_index[tag.lower()] for tag in tags))
return v
def all_package_classes(self) -> Generator[Type["spack.package_base.PackageBase"], None, None]:
def all_package_classes(self):
"""Iterator over all package *classes* in the repository.
Use this with care, because loading packages is slow.
@@ -1213,7 +1184,7 @@ def all_package_classes(self) -> Generator[Type["spack.package_base.PackageBase"
for name in self.all_package_names():
yield self.get_pkg_class(name)
def exists(self, pkg_name: str) -> bool:
def exists(self, pkg_name):
"""Whether a package with the supplied name exists."""
if pkg_name is None:
return False
@@ -1230,22 +1201,28 @@ def last_mtime(self):
"""Time a package file in this repo was last updated."""
return self._pkg_checker.last_mtime()
def is_virtual(self, pkg_name: str) -> bool:
def is_virtual(self, pkg_name):
"""Return True if the package with this name is virtual, False otherwise.
This function use the provider index. If calling from a code block that
is used to construct the provider index use the ``is_virtual_safe`` function.
Args:
pkg_name (str): name of the package we want to check
"""
return pkg_name in self.provider_index
def is_virtual_safe(self, pkg_name: str) -> bool:
def is_virtual_safe(self, pkg_name):
"""Return True if the package with this name is virtual, False otherwise.
This function doesn't use the provider index.
Args:
pkg_name (str): name of the package we want to check
"""
return not self.exists(pkg_name) or self.get_pkg_class(pkg_name).virtual
def get_pkg_class(self, pkg_name: str) -> Type["spack.package_base.PackageBase"]:
def get_pkg_class(self, pkg_name):
"""Get the class for the package out of its module.
First loads (or fetches from cache) a module for the
@@ -1257,8 +1234,7 @@ def get_pkg_class(self, pkg_name: str) -> Type["spack.package_base.PackageBase"]
fullname = f"{self.full_namespace}.{pkg_name}"
try:
with REPOS_FINDER.switch_repo(self._finder or self):
module = importlib.import_module(fullname)
module = importlib.import_module(fullname)
except ImportError:
raise UnknownPackageError(fullname)
except Exception as e:
@@ -1269,21 +1245,26 @@ def get_pkg_class(self, pkg_name: str) -> Type["spack.package_base.PackageBase"]
if not inspect.isclass(cls):
tty.die(f"{pkg_name}.{class_name} is not a class")
# Clear any prior changes to class attributes in case the class was loaded from the
# same repo, but with different overrides
new_cfg_settings = (
spack.config.get("packages").get(pkg_name, {}).get("package_attributes", {})
)
overridden_attrs = getattr(cls, "overridden_attrs", {})
attrs_exclusively_from_config = getattr(cls, "attrs_exclusively_from_config", [])
# Clear any prior changes to class attributes in case the config has
# since changed
for key, val in overridden_attrs.items():
setattr(cls, key, val)
for key in attrs_exclusively_from_config:
delattr(cls, key)
# Keep track of every class attribute that is overridden: if different overrides
# dictionaries are used on the same physical repo, we make sure to restore the original
# config values
# Keep track of every class attribute that is overridden by the config:
# if the config changes between calls to this method, we make sure to
# restore the original config values (in case the new config no longer
# sets attributes that it used to)
new_overridden_attrs = {}
new_attrs_exclusively_from_config = set()
for key, val in self.overrides.get(pkg_name, {}).items():
for key, val in new_cfg_settings.items():
if hasattr(cls, key):
new_overridden_attrs[key] = getattr(cls, key)
else:
@@ -1310,13 +1291,13 @@ def partition_package_name(self, pkg_name: str) -> Tuple[str, str]:
return namespace, pkg_name
def __str__(self) -> str:
return f"Repo '{self.namespace}' at {self.root}"
def __str__(self):
return "[Repo '%s' at '%s']" % (self.namespace, self.root)
def __repr__(self) -> str:
def __repr__(self):
return self.__str__()
def __contains__(self, pkg_name: str) -> bool:
def __contains__(self, pkg_name):
return self.exists(pkg_name)
@@ -1392,17 +1373,12 @@ def create_repo(root, namespace=None, subdir=packages_dir_name):
return full_path, namespace
def from_path(path: str) -> "Repo":
"""Returns a repository from the path passed as input. Injects the global misc cache."""
return Repo(path, cache=spack.caches.MISC_CACHE)
def create_or_construct(path, namespace=None):
"""Create a repository, or just return a Repo if it already exists."""
if not os.path.exists(path):
fs.mkdirp(path)
create_repo(path, namespace)
return from_path(path)
return Repo(path)
def _path(configuration=None):
@@ -1411,9 +1387,7 @@ def _path(configuration=None):
return create(configuration=configuration)
def create(
configuration: Union["spack.config.Configuration", llnl.util.lang.Singleton]
) -> RepoPath:
def create(configuration):
"""Create a RepoPath from a configuration object.
Args:
@@ -1422,17 +1396,7 @@ def create(
repo_dirs = configuration.get("repos")
if not repo_dirs:
raise NoRepoConfiguredError("Spack configuration contains no package repositories.")
overrides = {}
for pkg_name, data in configuration.get("packages").items():
if pkg_name == "all":
continue
value = data.get("package_attributes", {})
if not value:
continue
overrides[pkg_name] = value
return RepoPath(*repo_dirs, cache=spack.caches.MISC_CACHE, overrides=overrides)
return RepoPath(*repo_dirs)
#: Singleton repo path instance
@@ -1449,20 +1413,20 @@ def all_package_names(include_virtuals=False):
@contextlib.contextmanager
def use_repositories(
*paths_and_repos: Union[str, Repo], override: bool = True
) -> Generator[RepoPath, None, None]:
def use_repositories(*paths_and_repos, **kwargs):
"""Use the repositories passed as arguments within the context manager.
Args:
*paths_and_repos: paths to the repositories to be used, or
already constructed Repo objects
override: if True use only the repositories passed as input,
override (bool): if True use only the repositories passed as input,
if False add them to the top of the list of current repositories.
Returns:
Corresponding RepoPath object
"""
global PATH
# TODO (Python 2.7): remove this kwargs on deprecation of Python 2.7 support
override = kwargs.get("override", True)
paths = [getattr(x, "root", x) for x in paths_and_repos]
scope_name = "use-repo-{}".format(uuid.uuid4())
repos_key = "repos:" if override else "repos"
@@ -1471,8 +1435,7 @@ def use_repositories(
)
PATH, saved = create(configuration=spack.config.CONFIG), PATH
try:
with REPOS_FINDER.switch_repo(PATH): # type: ignore
yield PATH
yield PATH
finally:
spack.config.CONFIG.remove_scope(scope_name=scope_name)
PATH = saved
@@ -1572,9 +1535,10 @@ class UnknownNamespaceError(UnknownEntityError):
"""Raised when we encounter an unknown namespace"""
def __init__(self, namespace, name=None):
msg, long_msg = f"Unknown namespace: {namespace}", None
msg, long_msg = "Unknown namespace: {}".format(namespace), None
if name == "yaml":
long_msg = f"Did you mean to specify a filename with './{namespace}.{name}'?"
long_msg = "Did you mean to specify a filename with './{}.{}'?"
long_msg = long_msg.format(namespace, name)
super().__init__(msg, long_msg)

View File

@@ -23,7 +23,6 @@
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.lang import elide_list
import spack
import spack.binary_distribution
@@ -622,9 +621,8 @@ def _external_config_with_implicit_externals(configuration):
class ErrorHandler:
def __init__(self, model, input_specs: List[spack.spec.Spec]):
def __init__(self, model):
self.model = model
self.input_specs = input_specs
self.full_model = None
def multiple_values_error(self, attribute, pkg):
@@ -711,13 +709,12 @@ def handle_error(self, msg, *args):
return msg
def message(self, errors) -> str:
input_specs = ", ".join(elide_list([f"`{s}`" for s in self.input_specs], 5))
header = f"failed to concretize {input_specs} for the following reasons:"
messages = (
f" {idx+1:2}. {self.handle_error(msg, *args)}"
messages = [
f" {idx+1: 2}. {self.handle_error(msg, *args)}"
for idx, (_, msg, args) in enumerate(errors)
)
return "\n".join((header, *messages))
]
header = "concretization failed for the following reasons:\n"
return "\n".join([header] + messages)
def raise_if_errors(self):
initial_error_args = extract_args(self.model, "error")
@@ -753,7 +750,7 @@ def on_model(model):
f"unexpected error during concretization [{str(e)}]. "
f"Please report a bug at https://github.com/spack/spack/issues"
)
raise spack.error.SpackError(msg) from e
raise spack.error.SpackError(msg)
raise UnsatisfiableSpecError(msg)
@@ -897,7 +894,7 @@ def on_model(model):
min_cost, best_model = min(models)
# first check for errors
error_handler = ErrorHandler(best_model, specs)
error_handler = ErrorHandler(best_model)
error_handler.raise_if_errors()
# build specs from spec attributes in the model

View File

@@ -611,18 +611,25 @@ do_not_impose(EffectID, node(X, Package))
% Virtual dependency weights
%-----------------------------------------------------------------------------
% A provider has different possible weights depending on its preference. This rule ensures that
% A provider may have different possible weights depending on whether it's an external
% or not, or on preferences expressed in packages.yaml etc. This rule ensures that
% we select the weight, among the possible ones, that minimizes the overall objective function.
1 { provider_weight(DependencyNode, VirtualNode, Weight) :
possible_provider_weight(DependencyNode, VirtualNode, Weight, _) } 1
:- provider(DependencyNode, VirtualNode), internal_error("Package provider weights must be unique").
% Any configured provider has a weight based on index in the preference list
% A provider that is an external can use a weight of 0
possible_provider_weight(DependencyNode, VirtualNode, 0, "external")
:- provider(DependencyNode, VirtualNode),
external(DependencyNode).
% A provider mentioned in the default configuration can use a weight
% according to its priority in the list of providers
possible_provider_weight(node(ProviderID, Provider), node(VirtualID, Virtual), Weight, "default")
:- provider(node(ProviderID, Provider), node(VirtualID, Virtual)),
default_provider_preference(Virtual, Provider, Weight).
% Any non-configured provider has a default weight of 100
% Any provider can use 100 as a weight, which is very high and discourage its use
possible_provider_weight(node(ProviderID, Provider), VirtualNode, 100, "fallback")
:- provider(node(ProviderID, Provider), VirtualNode).
@@ -1155,11 +1162,8 @@ target_weight(Target, 0)
node_target_weight(PackageNode, MinWeight)
:- attr("node", PackageNode),
attr("node_target", PackageNode, Target),
target(Target),
MinWeight = #min { Weight : target_weight(Target, Weight) }.
:- attr("node_target", PackageNode, Target), not node_target_weight(PackageNode, _).
% compatibility rules for targets among nodes
node_target_match(ParentNode, DependencyNode)
:- attr("depends_on", ParentNode, DependencyNode, Type), Type != "build",

View File

@@ -12,7 +12,6 @@
%=============================================================================
% macOS
os_compatible("sequoia", "sonoma").
os_compatible("sonoma", "ventura").
os_compatible("ventura", "monterey").
os_compatible("monterey", "bigsur").

View File

@@ -1332,12 +1332,6 @@ def tree(
if color is None:
color = clr.get_color_when()
# reduce deptypes over all in-edges when covering nodes
if show_types and cover == "nodes":
deptype_lookup: Dict[str, dt.DepFlag] = collections.defaultdict(dt.DepFlag)
for edge in traverse.traverse_edges(specs, cover="edges", deptype=deptypes, root=False):
deptype_lookup[edge.spec.dag_hash()] |= edge.depflag
for d, dep_spec in traverse.traverse_tree(
sorted(specs), cover=cover, deptype=deptypes, depth_first=depth_first, key=key
):
@@ -1364,7 +1358,11 @@ def tree(
if show_types:
if cover == "nodes":
depflag = deptype_lookup[dep_spec.spec.dag_hash()]
# when only covering nodes, we merge dependency types
# from all dependents before showing them.
depflag = 0
for ds in node.edges_from_dependents():
depflag |= ds.depflag
else:
# when covering edges or paths, we show dependency
# types only for the edge through which we visited
@@ -4260,21 +4258,29 @@ def __getitem__(self, name: str):
csv = query_parameters.pop().strip()
query_parameters = re.split(r"\s*,\s*", csv)
# In some cases a package appears multiple times in the same DAG for *distinct*
# specs. For example, a build-type dependency may itself depend on a package
# the current spec depends on, but their specs may differ. Therefore we iterate
# in an order here that prioritizes the build, test and runtime dependencies;
# only when we don't find the package do we consider the full DAG.
order = lambda: itertools.chain(
self.traverse_edges(deptype=dt.LINK, order="breadth", cover="edges"),
self.edges_to_dependencies(depflag=dt.BUILD | dt.RUN | dt.TEST),
self.traverse_edges(deptype=dt.ALL, order="breadth", cover="edges"),
self.traverse(deptype="link"),
self.dependencies(deptype=dt.BUILD | dt.RUN | dt.TEST),
self.traverse(), # fall back to a full search
)
# Consider runtime dependencies and direct build/test deps before transitive dependencies,
# and prefer matches closest to the root.
try:
child: Spec = next(
e.spec
for e in itertools.chain(
(e for e in order() if e.spec.name == name or name in e.virtuals),
# for historical reasons
(e for e in order() if e.spec.concrete and e.spec.package.provides(name)),
itertools.chain(
# Regular specs
(x for x in order() if x.name == name),
(
x
for x in order()
if (not x.virtual)
and any(name in edge.virtuals for edge in x.edges_from_dependents())
),
(x for x in order() if (not x.virtual) and x.package.provides(name)),
)
)
except StopIteration:
@@ -4650,7 +4656,7 @@ def colored_str(self):
spec_str = " ^".join(root_str + sorted_dependencies)
return spec_str.strip()
def install_status(self) -> InstallStatus:
def install_status(self):
"""Helper for tree to print DB install status."""
if not self.concrete:
return InstallStatus.absent

View File

@@ -371,6 +371,7 @@ def use_store(
data.update(extra_data)
# Swap the store with the one just constructed and return it
ensure_singleton_created()
spack.config.CONFIG.push_scope(
spack.config.InternalConfigScope(name=scope_name, data={"config": {"install_tree": data}})
)

View File

@@ -79,11 +79,9 @@ def restore(self):
self.test_state.restore()
spack.main.spack_working_dir = self.spack_working_dir
env = pickle.load(self.serialized_env) if _SERIALIZE else self.env
pkg = pickle.load(self.serialized_pkg) if _SERIALIZE else self.pkg
if env:
spack.environment.activate(env)
# Order of operation is important, since the package might be retrieved
# from a repo defined within the environment configuration
pkg = pickle.load(self.serialized_pkg) if _SERIALIZE else self.pkg
return pkg

View File

@@ -213,9 +213,7 @@ def test_satisfy_strict_constraint_when_not_concrete(architecture_tuple, constra
str(archspec.cpu.host().family) != "x86_64", reason="tests are for x86_64 uarch ranges"
)
def test_concretize_target_ranges(root_target_range, dep_target_range, result, monkeypatch):
spec = Spec(
f"pkg-a %gcc@10 foobar=bar target={root_target_range} ^pkg-b target={dep_target_range}"
)
spec = Spec(f"a %gcc@10 foobar=bar target={root_target_range} ^b target={dep_target_range}")
with spack.concretize.disable_compiler_existence_check():
spec.concretize()
assert spec.target == spec["pkg-b"].target == result
assert spec.target == spec["b"].target == result

View File

@@ -105,18 +105,18 @@ def config_directory(tmpdir_factory):
@pytest.fixture(scope="function")
def default_config(tmpdir, config_directory, monkeypatch, install_mockery):
# This fixture depends on install_mockery to ensure
def default_config(tmpdir, config_directory, monkeypatch, install_mockery_mutable_config):
# This fixture depends on install_mockery_mutable_config to ensure
# there is a clear order of initialization. The substitution of the
# config scopes here is done on top of the substitution that comes with
# install_mockery
# install_mockery_mutable_config
mutable_dir = tmpdir.mkdir("mutable_config").join("tmp")
config_directory.copy(mutable_dir)
cfg = spack.config.Configuration(
*[
spack.config.DirectoryConfigScope(name, str(mutable_dir))
for name in [f"site/{platform.system().lower()}", "site", "user"]
spack.config.ConfigScope(name, str(mutable_dir))
for name in ["site/%s" % platform.system().lower(), "site", "user"]
]
)
@@ -398,7 +398,9 @@ def fake_dag_hash(spec, length=None):
return "tal4c7h4z0gqmixb1eqa92mjoybxn5l6"[:length]
@pytest.mark.usefixtures("install_mockery", "mock_packages", "mock_fetch", "test_mirror")
@pytest.mark.usefixtures(
"install_mockery_mutable_config", "mock_packages", "mock_fetch", "test_mirror"
)
def test_spec_needs_rebuild(monkeypatch, tmpdir):
"""Make sure needs_rebuild properly compares remote hash
against locally computed one, avoiding unnecessary rebuilds"""
@@ -427,7 +429,7 @@ def test_spec_needs_rebuild(monkeypatch, tmpdir):
assert rebuild
@pytest.mark.usefixtures("install_mockery", "mock_packages", "mock_fetch")
@pytest.mark.usefixtures("install_mockery_mutable_config", "mock_packages", "mock_fetch")
def test_generate_index_missing(monkeypatch, tmpdir, mutable_config):
"""Ensure spack buildcache index only reports available packages"""
@@ -585,7 +587,9 @@ def test_update_sbang(tmpdir, test_mirror):
str(archspec.cpu.host().family) != "x86_64",
reason="test data uses gcc 4.5.0 which does not support aarch64",
)
def test_install_legacy_buildcache_layout(mutable_config, compiler_factory, install_mockery):
def test_install_legacy_buildcache_layout(
mutable_config, compiler_factory, install_mockery_mutable_config
):
"""Legacy buildcache layout involved a nested archive structure
where the .spack file contained a repeated spec.json and another
compressed archive file containing the install tree. This test

View File

@@ -228,25 +228,3 @@ def test_source_is_disabled(mutable_config):
spack.config.add("bootstrap:trusted:{0}:{1}".format(conf["name"], False))
with pytest.raises(ValueError):
spack.bootstrap.core.source_is_enabled_or_raise(conf)
@pytest.mark.regression("45247")
def test_use_store_does_not_try_writing_outside_root(tmp_path, monkeypatch, mutable_config):
"""Tests that when we use the 'use_store' context manager, there is no attempt at creating
a Store outside the given root.
"""
initial_store = mutable_config.get("config:install_tree:root")
user_store = tmp_path / "store"
fn = spack.store.Store.__init__
def _checked_init(self, root, *args, **kwargs):
fn(self, root, *args, **kwargs)
assert self.root == str(user_store)
monkeypatch.setattr(spack.store.Store, "__init__", _checked_init)
spack.store.reinitialize()
with spack.store.use_store(user_store):
assert spack.config.CONFIG.get("config:install_tree:root") == str(user_store)
assert spack.config.CONFIG.get("config:install_tree:root") == initial_store

View File

@@ -177,7 +177,7 @@ def _set_wrong_cc(x):
def test_setup_dependent_package_inherited_modules(
working_env, mock_packages, install_mockery, mock_fetch
config, working_env, mock_packages, install_mockery, mock_fetch
):
# This will raise on regression
s = spack.spec.Spec("cmake-client-inheritor").concretized()
@@ -457,14 +457,14 @@ def test_parallel_false_is_not_propagating(default_mock_concretization):
# a foobar=bar (parallel = False)
# |
# b (parallel =True)
s = default_mock_concretization("pkg-a foobar=bar")
s = default_mock_concretization("a foobar=bar")
spack.build_environment.set_package_py_globals(s.package, context=Context.BUILD)
assert s["pkg-a"].package.module.make_jobs == 1
assert s["a"].package.module.make_jobs == 1
spack.build_environment.set_package_py_globals(s["pkg-b"].package, context=Context.BUILD)
assert s["pkg-b"].package.module.make_jobs == spack.build_environment.determine_number_of_jobs(
parallel=s["pkg-b"].package.parallel
spack.build_environment.set_package_py_globals(s["b"].package, context=Context.BUILD)
assert s["b"].package.module.make_jobs == spack.build_environment.determine_number_of_jobs(
parallel=s["b"].package.parallel
)

View File

@@ -94,10 +94,10 @@ def test_negative_ninja_check(self, input_dir, test_dir, concretize_and_setup):
@pytest.mark.not_on_windows("autotools not available on windows")
@pytest.mark.usefixtures("mock_packages")
@pytest.mark.usefixtures("config", "mock_packages")
class TestAutotoolsPackage:
def test_with_or_without(self, default_mock_concretization):
s = default_mock_concretization("pkg-a")
s = default_mock_concretization("a")
options = s.package.with_or_without("foo")
# Ensure that values that are not representing a feature
@@ -129,7 +129,7 @@ def activate(value):
assert "--without-lorem-ipsum" in options
def test_none_is_allowed(self, default_mock_concretization):
s = default_mock_concretization("pkg-a foo=none")
s = default_mock_concretization("a foo=none")
options = s.package.with_or_without("foo")
# Ensure that values that are not representing a feature
@@ -139,9 +139,11 @@ def test_none_is_allowed(self, default_mock_concretization):
assert "--without-baz" in options
assert "--no-fee" in options
def test_libtool_archive_files_are_deleted_by_default(self, mutable_database):
def test_libtool_archive_files_are_deleted_by_default(
self, default_mock_concretization, mutable_database
):
# Install a package that creates a mock libtool archive
s = Spec("libtool-deletion").concretized()
s = default_mock_concretization("libtool-deletion")
s.package.do_install(explicit=True)
# Assert the libtool archive is not there and we have
@@ -152,23 +154,25 @@ def test_libtool_archive_files_are_deleted_by_default(self, mutable_database):
assert libtool_deletion_log
def test_libtool_archive_files_might_be_installed_on_demand(
self, mutable_database, monkeypatch
self, mutable_database, monkeypatch, default_mock_concretization
):
# Install a package that creates a mock libtool archive,
# patch its package to preserve the installation
s = Spec("libtool-deletion").concretized()
s = default_mock_concretization("libtool-deletion")
monkeypatch.setattr(type(s.package.builder), "install_libtool_archives", True)
s.package.do_install(explicit=True)
# Assert libtool archives are installed
assert os.path.exists(s.package.builder.libtool_archive_file)
def test_autotools_gnuconfig_replacement(self, mutable_database):
def test_autotools_gnuconfig_replacement(self, default_mock_concretization, mutable_database):
"""
Tests whether only broken config.sub and config.guess are replaced with
files from working alternatives from the gnuconfig package.
"""
s = Spec("autotools-config-replacement +patch_config_files +gnuconfig").concretized()
s = default_mock_concretization(
"autotools-config-replacement +patch_config_files +gnuconfig"
)
s.package.do_install()
with open(os.path.join(s.prefix.broken, "config.sub")) as f:
@@ -183,11 +187,15 @@ def test_autotools_gnuconfig_replacement(self, mutable_database):
with open(os.path.join(s.prefix.working, "config.guess")) as f:
assert "gnuconfig version of config.guess" not in f.read()
def test_autotools_gnuconfig_replacement_disabled(self, mutable_database):
def test_autotools_gnuconfig_replacement_disabled(
self, default_mock_concretization, mutable_database
):
"""
Tests whether disabling patch_config_files
"""
s = Spec("autotools-config-replacement ~patch_config_files +gnuconfig").concretized()
s = default_mock_concretization(
"autotools-config-replacement ~patch_config_files +gnuconfig"
)
s.package.do_install()
with open(os.path.join(s.prefix.broken, "config.sub")) as f:

View File

@@ -199,7 +199,7 @@ def __call__(self, *args, **kwargs):
assert "Unable to merge {0}".format(c1) in err
def test_get_spec_filter_list(mutable_mock_env_path, mutable_mock_repo):
def test_get_spec_filter_list(mutable_mock_env_path, config, mutable_mock_repo):
"""Test that given an active environment and list of touched pkgs,
we get the right list of possibly-changed env specs"""
e1 = ev.create("test")
@@ -253,7 +253,7 @@ def test_get_spec_filter_list(mutable_mock_env_path, mutable_mock_repo):
@pytest.mark.regression("29947")
def test_affected_specs_on_first_concretization(mutable_mock_env_path, mock_packages):
def test_affected_specs_on_first_concretization(mutable_mock_env_path, mock_packages, config):
e = ev.create("first_concretization")
e.add("mpileaks~shared")
e.add("mpileaks+shared")
@@ -322,12 +322,12 @@ def test_ci_run_standalone_tests_missing_requirements(
@pytest.mark.not_on_windows("Reliance on bash script not supported on Windows")
def test_ci_run_standalone_tests_not_installed_junit(
tmp_path, repro_dir, working_env, mock_test_stage, capfd, mock_packages
tmp_path, repro_dir, working_env, default_mock_concretization, mock_test_stage, capfd
):
log_file = tmp_path / "junit.xml"
args = {
"log_file": str(log_file),
"job_spec": spack.spec.Spec("printing-package").concretized(),
"job_spec": default_mock_concretization("printing-package"),
"repro_dir": str(repro_dir),
"fail_fast": True,
}
@@ -340,13 +340,13 @@ def test_ci_run_standalone_tests_not_installed_junit(
@pytest.mark.not_on_windows("Reliance on bash script not supported on Windows")
def test_ci_run_standalone_tests_not_installed_cdash(
tmp_path, repro_dir, working_env, mock_test_stage, capfd, mock_packages
tmp_path, repro_dir, working_env, default_mock_concretization, mock_test_stage, capfd
):
"""Test run_standalone_tests with cdash and related options."""
log_file = tmp_path / "junit.xml"
args = {
"log_file": str(log_file),
"job_spec": spack.spec.Spec("printing-package").concretized(),
"job_spec": default_mock_concretization("printing-package"),
"repro_dir": str(repro_dir),
}

View File

@@ -48,6 +48,11 @@ def mock_get_specs_multiarch(database, monkeypatch):
monkeypatch.setattr(spack.binary_distribution, "update_cache_and_get_specs", lambda: specs)
def test_buildcache_preview_just_runs():
# TODO: remove in Spack 0.21
buildcache("preview", "mpileaks")
@pytest.mark.db
@pytest.mark.regression("13757")
def test_buildcache_list_duplicates(mock_get_specs, capsys):
@@ -184,7 +189,12 @@ def test_buildcache_autopush(tmp_path, install_mockery, mock_fetch):
def test_buildcache_sync(
mutable_mock_env_path, install_mockery, mock_packages, mock_fetch, mock_stage, tmpdir
mutable_mock_env_path,
install_mockery_mutable_config,
mock_packages,
mock_fetch,
mock_stage,
tmpdir,
):
"""
Make sure buildcache sync works in an environment-aware manner, ignoring
@@ -313,7 +323,7 @@ def manifest_insert(manifest, spec, dest_url):
def test_buildcache_create_install(
mutable_mock_env_path,
install_mockery,
install_mockery_mutable_config,
mock_packages,
mock_fetch,
mock_stage,

View File

@@ -83,6 +83,7 @@ def test_checksum_args(arguments, expected):
assert check == expected
@pytest.mark.not_on_windows("Not supported on Windows (yet)")
@pytest.mark.parametrize(
"arguments,expected",
[

View File

@@ -106,24 +106,24 @@ def test_specs_staging(config, tmpdir):
"""
builder = repo.MockRepositoryBuilder(tmpdir)
builder.add_package("pkg-g")
builder.add_package("pkg-f")
builder.add_package("pkg-e")
builder.add_package("pkg-d", dependencies=[("pkg-f", None, None), ("pkg-g", None, None)])
builder.add_package("pkg-c")
builder.add_package("pkg-b", dependencies=[("pkg-d", None, None), ("pkg-e", None, None)])
builder.add_package("pkg-a", dependencies=[("pkg-b", None, None), ("pkg-c", None, None)])
builder.add_package("g")
builder.add_package("f")
builder.add_package("e")
builder.add_package("d", dependencies=[("f", None, None), ("g", None, None)])
builder.add_package("c")
builder.add_package("b", dependencies=[("d", None, None), ("e", None, None)])
builder.add_package("a", dependencies=[("b", None, None), ("c", None, None)])
with repo.use_repositories(builder.root):
spec_a = Spec("pkg-a").concretized()
spec_a = Spec("a").concretized()
spec_a_label = ci._spec_ci_label(spec_a)
spec_b_label = ci._spec_ci_label(spec_a["pkg-b"])
spec_c_label = ci._spec_ci_label(spec_a["pkg-c"])
spec_d_label = ci._spec_ci_label(spec_a["pkg-d"])
spec_e_label = ci._spec_ci_label(spec_a["pkg-e"])
spec_f_label = ci._spec_ci_label(spec_a["pkg-f"])
spec_g_label = ci._spec_ci_label(spec_a["pkg-g"])
spec_b_label = ci._spec_ci_label(spec_a["b"])
spec_c_label = ci._spec_ci_label(spec_a["c"])
spec_d_label = ci._spec_ci_label(spec_a["d"])
spec_e_label = ci._spec_ci_label(spec_a["e"])
spec_f_label = ci._spec_ci_label(spec_a["f"])
spec_g_label = ci._spec_ci_label(spec_a["g"])
spec_labels, dependencies, stages = ci.stage_spec_jobs([spec_a])
@@ -748,7 +748,7 @@ def test_ci_rebuild_mock_success(
tmpdir,
working_env,
mutable_mock_env_path,
install_mockery,
install_mockery_mutable_config,
mock_gnupghome,
mock_stage,
mock_fetch,
@@ -782,7 +782,7 @@ def test_ci_rebuild_mock_failure_to_push(
tmpdir,
working_env,
mutable_mock_env_path,
install_mockery,
install_mockery_mutable_config,
mock_gnupghome,
mock_stage,
mock_fetch,
@@ -820,7 +820,7 @@ def test_ci_rebuild(
tmpdir,
working_env,
mutable_mock_env_path,
install_mockery,
install_mockery_mutable_config,
mock_packages,
monkeypatch,
mock_gnupghome,
@@ -1019,7 +1019,7 @@ def fake_dl_method(spec, *args, **kwargs):
def test_ci_generate_mirror_override(
tmpdir,
mutable_mock_env_path,
install_mockery,
install_mockery_mutable_config,
mock_packages,
mock_fetch,
mock_stage,
@@ -1104,7 +1104,7 @@ def test_ci_generate_mirror_override(
def test_push_to_build_cache(
tmpdir,
mutable_mock_env_path,
install_mockery,
install_mockery_mutable_config,
mock_packages,
mock_fetch,
mock_stage,
@@ -1290,7 +1290,7 @@ def test_ci_generate_override_runner_attrs(
spack:
specs:
- flatten-deps
- pkg-a
- a
mirrors:
some-mirror: https://my.fake.mirror
ci:
@@ -1307,12 +1307,12 @@ def test_ci_generate_override_runner_attrs(
- match:
- dependency-install
- match:
- pkg-a
- a
build-job:
tags:
- specific-a-2
- match:
- pkg-a
- a
build-job-remove:
tags:
- toplevel2
@@ -1372,8 +1372,8 @@ def test_ci_generate_override_runner_attrs(
assert global_vars["SPACK_CHECKOUT_VERSION"] == git_version or "v0.20.0.test0"
for ci_key in yaml_contents.keys():
if ci_key.startswith("pkg-a"):
# Make sure pkg-a's attributes override variables, and all the
if ci_key.startswith("a"):
# Make sure a's attributes override variables, and all the
# scripts. Also, make sure the 'toplevel' tag doesn't
# appear twice, but that a's specific extra tag does appear
the_elt = yaml_contents[ci_key]
@@ -1781,7 +1781,7 @@ def test_ci_generate_read_broken_specs_url(
tmpdir, mutable_mock_env_path, install_mockery, mock_packages, monkeypatch, ci_base_environment
):
"""Verify that `broken-specs-url` works as intended"""
spec_a = Spec("pkg-a")
spec_a = Spec("a")
spec_a.concretize()
a_dag_hash = spec_a.dag_hash()
@@ -1807,7 +1807,7 @@ def test_ci_generate_read_broken_specs_url(
spack:
specs:
- flatten-deps
- pkg-a
- a
mirrors:
some-mirror: https://my.fake.mirror
ci:
@@ -1815,9 +1815,9 @@ def test_ci_generate_read_broken_specs_url(
pipeline-gen:
- submapping:
- match:
- pkg-a
- a
- flatten-deps
- pkg-b
- b
- dependency-install
build-job:
tags:

View File

@@ -72,6 +72,7 @@ def test_parse_spec_flags_with_spaces(specs, cflags, propagation, negated_varian
assert "~{0}".format(v) in s
@pytest.mark.usefixtures("config")
def test_match_spec_env(mock_packages, mutable_mock_env_path):
"""
Concretize a spec with non-default options in an environment. Make
@@ -80,42 +81,44 @@ def test_match_spec_env(mock_packages, mutable_mock_env_path):
"""
# Initial sanity check: we are planning on choosing a non-default
# value, so make sure that is in fact not the default.
check_defaults = spack.cmd.parse_specs(["pkg-a"], concretize=True)[0]
check_defaults = spack.cmd.parse_specs(["a"], concretize=True)[0]
assert not check_defaults.satisfies("foobar=baz")
e = ev.create("test")
e.add("pkg-a foobar=baz")
e.add("a foobar=baz")
e.concretize()
with e:
env_spec = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["pkg-a"])[0])
env_spec = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["a"])[0])
assert env_spec.satisfies("foobar=baz")
assert env_spec.concrete
@pytest.mark.usefixtures("config")
def test_multiple_env_match_raises_error(mock_packages, mutable_mock_env_path):
e = ev.create("test")
e.add("pkg-a foobar=baz")
e.add("pkg-a foobar=fee")
e.add("a foobar=baz")
e.add("a foobar=fee")
e.concretize()
with e:
with pytest.raises(ev.SpackEnvironmentError) as exc_info:
spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["pkg-a"])[0])
spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["a"])[0])
assert "matches multiple specs" in exc_info.value.message
@pytest.mark.usefixtures("config")
def test_root_and_dep_match_returns_root(mock_packages, mutable_mock_env_path):
e = ev.create("test")
e.add("pkg-b@0.9")
e.add("pkg-a foobar=bar") # Depends on b, should choose b@1.0
e.add("b@0.9")
e.add("a foobar=bar") # Depends on b, should choose b@1.0
e.concretize()
with e:
# This query matches the root b and b as a dependency of a. In that
# case the root instance should be preferred.
env_spec1 = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["pkg-b"])[0])
env_spec1 = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["b"])[0])
assert env_spec1.satisfies("@0.9")
env_spec2 = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["pkg-b@1.0"])[0])
env_spec2 = spack.cmd.matching_spec_from_env(spack.cmd.parse_specs(["b@1.0"])[0])
assert env_spec2

View File

@@ -10,7 +10,7 @@
from spack import spack_version
from spack.main import SpackCommand
pytestmark = pytest.mark.usefixtures("mutable_config", "mutable_mock_repo")
pytestmark = pytest.mark.usefixtures("config", "mutable_mock_repo")
env = SpackCommand("env")
add = SpackCommand("add")
@@ -51,8 +51,8 @@ def test_concretize_root_test_dependencies_are_concretized(unify, mutable_mock_e
with ev.read("test") as e:
e.unify = unify
add("pkg-a")
add("pkg-b")
add("a")
add("b")
concretize("--test", "root")
assert e.matching_spec("test-dependency")

View File

@@ -640,4 +640,4 @@ def update_config(data):
config("update", "-y", "config")
with ev.Environment(str(tmpdir)) as e:
assert not e.manifest.yaml_content["spack"]["config"]["ccache"]
assert not e.manifest.pristine_yaml_content["spack"]["config"]["ccache"]

View File

@@ -12,29 +12,29 @@
@pytest.fixture(scope="function")
def test_env(mutable_mock_env_path, mock_packages):
def test_env(mutable_mock_env_path, config, mock_packages):
ev.create("test")
with ev.read("test") as e:
e.add("pkg-a@2.0 foobar=bar ^pkg-b@1.0")
e.add("pkg-a@1.0 foobar=bar ^pkg-b@0.9")
e.add("a@2.0 foobar=bar ^b@1.0")
e.add("a@1.0 foobar=bar ^b@0.9")
e.concretize()
e.write()
def test_deconcretize_dep(test_env):
with ev.read("test") as e:
deconcretize("-y", "pkg-b@1.0")
deconcretize("-y", "b@1.0")
specs = [s for s, _ in e.concretized_specs()]
assert len(specs) == 1
assert specs[0].satisfies("pkg-a@1.0")
assert specs[0].satisfies("a@1.0")
def test_deconcretize_all_dep(test_env):
with ev.read("test") as e:
with pytest.raises(SpackCommandError):
deconcretize("-y", "pkg-b")
deconcretize("-y", "--all", "pkg-b")
deconcretize("-y", "b")
deconcretize("-y", "--all", "b")
specs = [s for s, _ in e.concretized_specs()]
assert len(specs) == 0
@@ -42,27 +42,27 @@ def test_deconcretize_all_dep(test_env):
def test_deconcretize_root(test_env):
with ev.read("test") as e:
output = deconcretize("-y", "--root", "pkg-b@1.0")
output = deconcretize("-y", "--root", "b@1.0")
assert "No matching specs to deconcretize" in output
assert len(e.concretized_order) == 2
deconcretize("-y", "--root", "pkg-a@2.0")
deconcretize("-y", "--root", "a@2.0")
specs = [s for s, _ in e.concretized_specs()]
assert len(specs) == 1
assert specs[0].satisfies("pkg-a@1.0")
assert specs[0].satisfies("a@1.0")
def test_deconcretize_all_root(test_env):
with ev.read("test") as e:
with pytest.raises(SpackCommandError):
deconcretize("-y", "--root", "pkg-a")
deconcretize("-y", "--root", "a")
output = deconcretize("-y", "--root", "--all", "pkg-b")
output = deconcretize("-y", "--root", "--all", "b")
assert "No matching specs to deconcretize" in output
assert len(e.concretized_order) == 2
deconcretize("-y", "--root", "--all", "pkg-a")
deconcretize("-y", "--root", "--all", "a")
specs = [s for s, _ in e.concretized_specs()]
assert len(specs) == 0

View File

@@ -14,6 +14,8 @@
deprecate = SpackCommand("deprecate")
find = SpackCommand("find")
pytestmark = pytest.mark.not_on_windows("does not run on windows")
def test_deprecate(mock_packages, mock_archive, mock_fetch, install_mockery):
install("libelf@0.8.13")

View File

@@ -20,7 +20,10 @@
install = SpackCommand("install")
env = SpackCommand("env")
pytestmark = [pytest.mark.disable_clean_stage_check]
pytestmark = [
pytest.mark.not_on_windows("does not run on windows"),
pytest.mark.disable_clean_stage_check,
]
def test_dev_build_basics(tmpdir, install_mockery):
@@ -93,7 +96,7 @@ def test_dev_build_until_last_phase(tmpdir, install_mockery):
assert os.path.exists(str(tmpdir))
def test_dev_build_before_until(tmpdir, install_mockery):
def test_dev_build_before_until(tmpdir, install_mockery, capsys):
spec = spack.spec.Spec(f"dev-build-test-install@0.0.0 dev_path={tmpdir}").concretized()
with tmpdir.as_cwd():
@@ -126,7 +129,7 @@ def test_dev_build_drop_in(tmpdir, mock_packages, monkeypatch, install_mockery,
monkeypatch.setattr(os, "execvp", print_spack_cc)
with tmpdir.as_cwd():
output = dev_build("-b", "edit", "--drop-in", "sh", "dev-build-test-install@0.0.0")
assert os.path.join("lib", "spack", "env") in output
assert "lib/spack/env" in output
def test_dev_build_fails_already_installed(tmpdir, install_mockery):

View File

@@ -181,6 +181,7 @@ def test_diff_cmd(install_mockery, mock_fetch, mock_archive, mock_packages):
assert ["hash", "mpileaks %s" % specB.dag_hash()] in c["b_not_a"]
@pytest.mark.not_on_windows("Not supported on Windows (yet)")
def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages):
"""Test with and without the --first option"""
install_cmd("mpileaks")

View File

@@ -15,9 +15,9 @@
def test_edit_packages(monkeypatch, mock_packages: spack.repo.RepoPath):
"""Test spack edit pkg-a pkg-b"""
path_a = mock_packages.filename_for_package_name("pkg-a")
path_b = mock_packages.filename_for_package_name("pkg-b")
"""Test spack edit a b"""
path_a = mock_packages.filename_for_package_name("a")
path_b = mock_packages.filename_for_package_name("b")
called = False
def editor(*args: str, **kwargs):
@@ -27,7 +27,7 @@ def editor(*args: str, **kwargs):
assert args[1] == path_b
monkeypatch.setattr(spack.util.editor, "editor", editor)
edit("pkg-a", "pkg-b")
edit("a", "b")
assert called

View File

@@ -28,9 +28,7 @@
import spack.package_base
import spack.paths
import spack.repo
import spack.store
import spack.util.spack_json as sjson
import spack.util.spack_yaml
from spack.cmd.env import _env_create
from spack.main import SpackCommand, SpackCommandError
from spack.spec import Spec
@@ -42,7 +40,7 @@
# TODO-27021
# everything here uses the mock_env_path
pytestmark = [
pytest.mark.usefixtures("mutable_config", "mutable_mock_env_path", "mutable_mock_repo"),
pytest.mark.usefixtures("mutable_mock_env_path", "config", "mutable_mock_repo"),
pytest.mark.maybeslow,
pytest.mark.not_on_windows("Envs unsupported on Window"),
]
@@ -503,7 +501,7 @@ def test_env_install_two_specs_same_dep(install_mockery, mock_fetch, tmpdir, cap
"""\
spack:
specs:
- pkg-a
- a
- depb
"""
)
@@ -522,8 +520,8 @@ def test_env_install_two_specs_same_dep(install_mockery, mock_fetch, tmpdir, cap
depb = spack.store.STORE.db.query_one("depb", installed=True)
assert depb, "Expected depb to be installed"
a = spack.store.STORE.db.query_one("pkg-a", installed=True)
assert a, "Expected pkg-a to be installed"
a = spack.store.STORE.db.query_one("a", installed=True)
assert a, "Expected a to be installed"
def test_remove_after_concretize():
@@ -814,6 +812,7 @@ def test_init_from_yaml(environment_from_manifest):
assert not e2.specs_by_hash
@pytest.mark.usefixtures("config")
def test_env_view_external_prefix(tmp_path, mutable_database, mock_packages):
fake_prefix = tmp_path / "a-prefix"
fake_bin = fake_prefix / "bin"
@@ -826,7 +825,7 @@ def test_env_view_external_prefix(tmp_path, mutable_database, mock_packages):
"""\
spack:
specs:
- pkg-a
- a
view: true
"""
)
@@ -834,9 +833,9 @@ def test_env_view_external_prefix(tmp_path, mutable_database, mock_packages):
external_config = io.StringIO(
"""\
packages:
pkg-a:
a:
externals:
- spec: pkg-a@2.0
- spec: a@2.0
prefix: {a_prefix}
buildable: false
""".format(
@@ -1560,6 +1559,7 @@ def test_uninstall_removes_from_env(mock_stage, mock_fetch, install_mockery):
assert not test.user_specs
@pytest.mark.usefixtures("config")
def test_indirect_build_dep(tmp_path):
"""Simple case of X->Y->Z where Y is a build/link dep and Z is a
build-only dep. Make sure this concrete DAG is preserved when writing the
@@ -1587,6 +1587,7 @@ def test_indirect_build_dep(tmp_path):
assert x_env_spec == x_concretized
@pytest.mark.usefixtures("config")
def test_store_different_build_deps(tmp_path):
r"""Ensure that an environment can store two instances of a build-only
dependency::
@@ -2327,7 +2328,7 @@ def test_stack_yaml_force_remove_from_matrix(tmpdir):
assert mpileaks_spec not in after_conc
def test_stack_concretize_extraneous_deps(tmpdir, mock_packages):
def test_stack_concretize_extraneous_deps(tmpdir, config, mock_packages):
# FIXME: The new concretizer doesn't handle yet soft
# FIXME: constraints for stacks
# FIXME: This now works for statically-determinable invalid deps
@@ -2366,7 +2367,7 @@ def test_stack_concretize_extraneous_deps(tmpdir, mock_packages):
assert concrete.satisfies("^mpi")
def test_stack_concretize_extraneous_variants(tmpdir, mock_packages):
def test_stack_concretize_extraneous_variants(tmpdir, config, mock_packages):
filename = str(tmpdir.join("spack.yaml"))
with open(filename, "w") as f:
f.write(
@@ -2398,7 +2399,7 @@ def test_stack_concretize_extraneous_variants(tmpdir, mock_packages):
assert concrete.variants["shared"].value == user.variants["shared"].value
def test_stack_concretize_extraneous_variants_with_dash(tmpdir, mock_packages):
def test_stack_concretize_extraneous_variants_with_dash(tmpdir, config, mock_packages):
filename = str(tmpdir.join("spack.yaml"))
with open(filename, "w") as f:
f.write(
@@ -3749,7 +3750,7 @@ def test_environment_query_spec_by_hash(mock_stage, mock_fetch, install_mockery)
@pytest.mark.parametrize("lockfile", ["v1", "v2", "v3"])
def test_read_old_lock_and_write_new(tmpdir, lockfile):
def test_read_old_lock_and_write_new(config, tmpdir, lockfile):
# v1 lockfiles stored by a coarse DAG hash that did not include build deps.
# They could not represent multiple build deps with different build hashes.
#
@@ -3815,7 +3816,7 @@ def test_read_old_lock_and_write_new(tmpdir, lockfile):
assert old_hashes == hashes
def test_read_v1_lock_creates_backup(tmp_path):
def test_read_v1_lock_creates_backup(config, tmp_path):
"""When reading a version-1 lockfile, make sure that a backup of that file
is created.
"""
@@ -4198,7 +4199,7 @@ def test_env_include_packages_url(
assert "openmpi" in cfg["all"]["providers"]["mpi"]
def test_relative_view_path_on_command_line_is_made_absolute(tmp_path):
def test_relative_view_path_on_command_line_is_made_absolute(tmp_path, config):
with fs.working_dir(str(tmp_path)):
env("create", "--with-view", "view", "--dir", "env")
environment = ev.Environment(os.path.join(".", "env"))

View File

@@ -24,7 +24,7 @@ def python_database(mock_packages, mutable_database):
@pytest.mark.not_on_windows("All Fetchers Failed")
@pytest.mark.db
def test_extensions(mock_packages, python_database, capsys):
def test_extensions(mock_packages, python_database, config, capsys):
ext2 = Spec("py-extension2").concretized()
def check_output(ni):

View File

@@ -11,7 +11,6 @@
from llnl.util.filesystem import getuid, touch
import spack
import spack.cmd.external
import spack.detection
import spack.detection.path
from spack.main import SpackCommand
@@ -312,29 +311,3 @@ def test_failures_in_scanning_do_not_result_in_an_error(
assert "cmake" in output
assert "3.23.3" in output
assert "3.19.1" not in output
def test_detect_virtuals(mock_executable, mutable_config, monkeypatch):
"""Test whether external find --not-buildable sets virtuals as non-buildable (unless user
config sets them to buildable)"""
mpich = mock_executable("mpichversion", output="echo MPICH Version: 4.0.2")
prefix = os.path.dirname(mpich)
external("find", "--path", prefix, "--not-buildable", "mpich")
# Check that mpich was correctly detected
mpich = mutable_config.get("packages:mpich")
assert mpich["buildable"] is False
assert Spec(mpich["externals"][0]["spec"]).satisfies("mpich@4.0.2")
# Check that the virtual package mpi was marked as non-buildable
assert mutable_config.get("packages:mpi:buildable") is False
# Delete the mpich entry, and set mpi explicitly to buildable
mutable_config.set("packages:mpich", {})
mutable_config.set("packages:mpi:buildable", True)
# Run the detection again
external("find", "--path", prefix, "--not-buildable", "mpich")
# Check that the mpi:buildable entry was not overwritten
assert mutable_config.get("packages:mpi:buildable") is True

View File

@@ -9,9 +9,7 @@
from spack.main import SpackCommand, SpackCommandError
# everything here uses the mock_env_path
pytestmark = pytest.mark.usefixtures(
"mutable_mock_env_path", "mutable_config", "mutable_mock_repo"
)
pytestmark = pytest.mark.usefixtures("mutable_mock_env_path", "config", "mutable_mock_repo")
@pytest.mark.disable_clean_stage_check

View File

@@ -337,7 +337,7 @@ def test_find_command_basic_usage(database):
@pytest.mark.not_on_windows("envirnment is not yet supported on windows")
@pytest.mark.regression("9875")
def test_find_prefix_in_env(
mutable_mock_env_path, install_mockery, mock_fetch, mock_packages, mock_archive
mutable_mock_env_path, install_mockery, mock_fetch, mock_packages, mock_archive, config
):
"""Test `find` formats requiring concrete specs work in environments."""
env("create", "test")
@@ -349,7 +349,7 @@ def test_find_prefix_in_env(
# Would throw error on regression
def test_find_specs_include_concrete_env(mutable_mock_env_path, mutable_mock_repo, tmpdir):
def test_find_specs_include_concrete_env(mutable_mock_env_path, config, mutable_mock_repo, tmpdir):
path = tmpdir.join("spack.yaml")
with tmpdir.as_cwd():
@@ -393,7 +393,9 @@ def test_find_specs_include_concrete_env(mutable_mock_env_path, mutable_mock_rep
assert "libelf" in output
def test_find_specs_nested_include_concrete_env(mutable_mock_env_path, mutable_mock_repo, tmpdir):
def test_find_specs_nested_include_concrete_env(
mutable_mock_env_path, config, mutable_mock_repo, tmpdir
):
path = tmpdir.join("spack.yaml")
with tmpdir.as_cwd():

View File

@@ -20,13 +20,13 @@
@pytest.mark.db
def test_gc_without_build_dependency(mutable_database):
def test_gc_without_build_dependency(config, mutable_database):
assert "There are no unused specs." in gc("-yb")
assert "There are no unused specs." in gc("-y")
@pytest.mark.db
def test_gc_with_build_dependency(mutable_database):
def test_gc_with_build_dependency(config, mutable_database):
s = spack.spec.Spec("simple-inheritance")
s.concretize()
s.package.do_install(fake=True, explicit=True)
@@ -37,7 +37,7 @@ def test_gc_with_build_dependency(mutable_database):
@pytest.mark.db
def test_gc_with_environment(mutable_database, mutable_mock_env_path):
def test_gc_with_environment(config, mutable_database, mutable_mock_env_path):
s = spack.spec.Spec("simple-inheritance")
s.concretize()
s.package.do_install(fake=True, explicit=True)
@@ -53,7 +53,7 @@ def test_gc_with_environment(mutable_database, mutable_mock_env_path):
@pytest.mark.db
def test_gc_with_build_dependency_in_environment(mutable_database, mutable_mock_env_path):
def test_gc_with_build_dependency_in_environment(config, mutable_database, mutable_mock_env_path):
s = spack.spec.Spec("simple-inheritance")
s.concretize()
s.package.do_install(fake=True, explicit=True)
@@ -78,7 +78,7 @@ def test_gc_with_build_dependency_in_environment(mutable_database, mutable_mock_
@pytest.mark.db
def test_gc_except_any_environments(mutable_database, mutable_mock_env_path):
def test_gc_except_any_environments(config, mutable_database, mutable_mock_env_path):
"""Tests whether the garbage collector can remove all specs except those still needed in some
environment (needed in the sense of roots + link/run deps)."""
assert mutable_database.query_local("zmpi")
@@ -105,7 +105,7 @@ def test_gc_except_any_environments(mutable_database, mutable_mock_env_path):
@pytest.mark.db
def test_gc_except_specific_environments(mutable_database, mutable_mock_env_path):
def test_gc_except_specific_environments(config, mutable_database, mutable_mock_env_path):
s = spack.spec.Spec("simple-inheritance")
s.concretize()
s.package.do_install(fake=True, explicit=True)
@@ -125,14 +125,14 @@ def test_gc_except_specific_environments(mutable_database, mutable_mock_env_path
@pytest.mark.db
def test_gc_except_nonexisting_dir_env(mutable_database, mutable_mock_env_path, tmpdir):
def test_gc_except_nonexisting_dir_env(config, mutable_database, mutable_mock_env_path, tmpdir):
output = gc("-ye", tmpdir.strpath, fail_on_error=False)
assert "No such environment" in output
gc.returncode == 1
@pytest.mark.db
def test_gc_except_specific_dir_env(mutable_database, mutable_mock_env_path, tmpdir):
def test_gc_except_specific_dir_env(config, mutable_database, mutable_mock_env_path, tmpdir):
s = spack.spec.Spec("simple-inheritance")
s.concretize()
s.package.do_install(fake=True, explicit=True)

View File

@@ -49,7 +49,7 @@ def noop(*args, **kwargs):
def test_install_package_and_dependency(
tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery
):
log = "test"
with tmpdir.as_cwd():
@@ -89,11 +89,11 @@ def check(pkg):
assert pkg.run_tests
monkeypatch.setattr(spack.package_base.PackageBase, "unit_test_check", check)
install("--test=all", "pkg-a")
install("--test=all", "a")
def test_install_package_already_installed(
tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery
):
with tmpdir.as_cwd():
install("libdwarf")
@@ -149,7 +149,7 @@ def test_package_output(tmpdir, capsys, install_mockery, mock_fetch):
@pytest.mark.disable_clean_stage_check
def test_install_output_on_build_error(
mock_packages, mock_archive, mock_fetch, install_mockery, capfd
mock_packages, mock_archive, mock_fetch, config, install_mockery, capfd
):
"""
This test used to assume receiving full output, but since we've updated
@@ -163,7 +163,9 @@ def test_install_output_on_build_error(
@pytest.mark.disable_clean_stage_check
def test_install_output_on_python_error(mock_packages, mock_archive, mock_fetch, install_mockery):
def test_install_output_on_python_error(
mock_packages, mock_archive, mock_fetch, config, install_mockery
):
out = install("failing-build", fail_on_error=False)
assert isinstance(install.error, spack.build_environment.ChildError)
assert install.error.name == "InstallError"
@@ -171,7 +173,7 @@ def test_install_output_on_python_error(mock_packages, mock_archive, mock_fetch,
@pytest.mark.disable_clean_stage_check
def test_install_with_source(mock_packages, mock_archive, mock_fetch, install_mockery):
def test_install_with_source(mock_packages, mock_archive, mock_fetch, config, install_mockery):
"""Verify that source has been copied into place."""
install("--source", "--keep-stage", "trivial-install-test-package")
spec = Spec("trivial-install-test-package").concretized()
@@ -181,7 +183,7 @@ def test_install_with_source(mock_packages, mock_archive, mock_fetch, install_mo
)
def test_install_env_variables(mock_packages, mock_archive, mock_fetch, install_mockery):
def test_install_env_variables(mock_packages, mock_archive, mock_fetch, config, install_mockery):
spec = Spec("libdwarf")
spec.concretize()
install("libdwarf")
@@ -189,7 +191,9 @@ def test_install_env_variables(mock_packages, mock_archive, mock_fetch, install_
@pytest.mark.disable_clean_stage_check
def test_show_log_on_error(mock_packages, mock_archive, mock_fetch, install_mockery, capfd):
def test_show_log_on_error(
mock_packages, mock_archive, mock_fetch, config, install_mockery, capfd
):
"""
Make sure --show-log-on-error works.
"""
@@ -202,7 +206,7 @@ def test_show_log_on_error(mock_packages, mock_archive, mock_fetch, install_mock
assert "See build log for details:" in out
def test_install_overwrite(mock_packages, mock_archive, mock_fetch, install_mockery):
def test_install_overwrite(mock_packages, mock_archive, mock_fetch, config, install_mockery):
# Try to install a spec and then to reinstall it.
spec = Spec("libdwarf")
spec.concretize()
@@ -236,7 +240,9 @@ def test_install_overwrite(mock_packages, mock_archive, mock_fetch, install_mock
assert fs.hash_directory(spec.prefix, ignore=ignores) != bad_md5
def test_install_overwrite_not_installed(mock_packages, mock_archive, mock_fetch, install_mockery):
def test_install_overwrite_not_installed(
mock_packages, mock_archive, mock_fetch, config, install_mockery
):
# Try to install a spec and then to reinstall it.
spec = Spec("libdwarf")
spec.concretize()
@@ -271,7 +277,9 @@ def test_install_commit(mock_git_version_info, install_mockery, mock_packages, m
assert content == "[0]" # contents are weird for another test
def test_install_overwrite_multiple(mock_packages, mock_archive, mock_fetch, install_mockery):
def test_install_overwrite_multiple(
mock_packages, mock_archive, mock_fetch, config, install_mockery
):
# Try to install a spec and then to reinstall it.
libdwarf = Spec("libdwarf")
libdwarf.concretize()
@@ -329,14 +337,18 @@ def test_install_overwrite_multiple(mock_packages, mock_archive, mock_fetch, ins
assert cm_hash != bad_cmake_md5
@pytest.mark.usefixtures("mock_packages", "mock_archive", "mock_fetch", "install_mockery")
@pytest.mark.usefixtures(
"mock_packages", "mock_archive", "mock_fetch", "config", "install_mockery"
)
def test_install_conflicts(conflict_spec):
# Make sure that spec with conflicts raises a SpackError
with pytest.raises(SpackError):
install(conflict_spec)
@pytest.mark.usefixtures("mock_packages", "mock_archive", "mock_fetch", "install_mockery")
@pytest.mark.usefixtures(
"mock_packages", "mock_archive", "mock_fetch", "config", "install_mockery"
)
def test_install_invalid_spec(invalid_spec):
# Make sure that invalid specs raise a SpackError
with pytest.raises(SpecSyntaxError, match="unexpected tokens"):
@@ -378,7 +390,9 @@ def test_install_from_file(spec, concretize, error_code, tmpdir):
@pytest.mark.disable_clean_stage_check
@pytest.mark.usefixtures("mock_packages", "mock_archive", "mock_fetch", "install_mockery")
@pytest.mark.usefixtures(
"mock_packages", "mock_archive", "mock_fetch", "config", "install_mockery"
)
@pytest.mark.parametrize(
"exc_typename,msg",
[("RuntimeError", "something weird happened"), ("ValueError", "spec is not concrete")],
@@ -434,6 +448,7 @@ def test_junit_output_with_errors(
mock_archive,
mock_fetch,
install_mockery,
config,
tmpdir,
monkeypatch,
):
@@ -494,7 +509,9 @@ def test_install_mix_cli_and_files(clispecs, filespecs, tmpdir):
assert install.returncode == 0
def test_extra_files_are_archived(mock_packages, mock_archive, mock_fetch, install_mockery):
def test_extra_files_are_archived(
mock_packages, mock_archive, mock_fetch, config, install_mockery
):
s = Spec("archive-files")
s.concretize()
@@ -553,96 +570,100 @@ def test_cdash_upload_build_error(tmpdir, mock_fetch, install_mockery, capfd):
@pytest.mark.disable_clean_stage_check
def test_cdash_upload_clean_build(tmpdir, mock_fetch, install_mockery, capfd):
# capfd interferes with Spack's capturing of e.g., Build.xml output
with capfd.disabled(), tmpdir.as_cwd():
install("--log-file=cdash_reports", "--log-format=cdash", "pkg-a")
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("pkg-a_Build.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert "</Build>" in content
assert "<Text>" not in content
with capfd.disabled():
with tmpdir.as_cwd():
install("--log-file=cdash_reports", "--log-format=cdash", "a")
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("a_Build.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert "</Build>" in content
assert "<Text>" not in content
@pytest.mark.disable_clean_stage_check
def test_cdash_upload_extra_params(tmpdir, mock_fetch, install_mockery, capfd):
# capfd interferes with Spack's capture of e.g., Build.xml output
with capfd.disabled(), tmpdir.as_cwd():
install(
"--log-file=cdash_reports",
"--log-format=cdash",
"--cdash-build=my_custom_build",
"--cdash-site=my_custom_site",
"--cdash-track=my_custom_track",
"pkg-a",
)
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("pkg-a_Build.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert 'Site BuildName="my_custom_build - pkg-a"' in content
assert 'Name="my_custom_site"' in content
assert "-my_custom_track" in content
with capfd.disabled():
with tmpdir.as_cwd():
install(
"--log-file=cdash_reports",
"--log-format=cdash",
"--cdash-build=my_custom_build",
"--cdash-site=my_custom_site",
"--cdash-track=my_custom_track",
"a",
)
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("a_Build.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert 'Site BuildName="my_custom_build - a"' in content
assert 'Name="my_custom_site"' in content
assert "-my_custom_track" in content
@pytest.mark.disable_clean_stage_check
def test_cdash_buildstamp_param(tmpdir, mock_fetch, install_mockery, capfd):
# capfd interferes with Spack's capture of e.g., Build.xml output
with capfd.disabled(), tmpdir.as_cwd():
cdash_track = "some_mocked_track"
buildstamp_format = "%Y%m%d-%H%M-{0}".format(cdash_track)
buildstamp = time.strftime(buildstamp_format, time.localtime(int(time.time())))
install(
"--log-file=cdash_reports",
"--log-format=cdash",
"--cdash-buildstamp={0}".format(buildstamp),
"pkg-a",
)
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("pkg-a_Build.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert buildstamp in content
with capfd.disabled():
with tmpdir.as_cwd():
cdash_track = "some_mocked_track"
buildstamp_format = "%Y%m%d-%H%M-{0}".format(cdash_track)
buildstamp = time.strftime(buildstamp_format, time.localtime(int(time.time())))
install(
"--log-file=cdash_reports",
"--log-format=cdash",
"--cdash-buildstamp={0}".format(buildstamp),
"a",
)
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("a_Build.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert buildstamp in content
@pytest.mark.disable_clean_stage_check
def test_cdash_install_from_spec_json(
tmpdir, mock_fetch, install_mockery, capfd, mock_packages, mock_archive
tmpdir, mock_fetch, install_mockery, capfd, mock_packages, mock_archive, config
):
# capfd interferes with Spack's capturing
with capfd.disabled(), tmpdir.as_cwd():
spec_json_path = str(tmpdir.join("spec.json"))
with capfd.disabled():
with tmpdir.as_cwd():
spec_json_path = str(tmpdir.join("spec.json"))
pkg_spec = Spec("pkg-a")
pkg_spec.concretize()
pkg_spec = Spec("a")
pkg_spec.concretize()
with open(spec_json_path, "w") as fd:
fd.write(pkg_spec.to_json(hash=ht.dag_hash))
with open(spec_json_path, "w") as fd:
fd.write(pkg_spec.to_json(hash=ht.dag_hash))
install(
"--log-format=cdash",
"--log-file=cdash_reports",
"--cdash-build=my_custom_build",
"--cdash-site=my_custom_site",
"--cdash-track=my_custom_track",
"-f",
spec_json_path,
)
install(
"--log-format=cdash",
"--log-file=cdash_reports",
"--cdash-build=my_custom_build",
"--cdash-site=my_custom_site",
"--cdash-track=my_custom_track",
"-f",
spec_json_path,
)
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("pkg-a_Configure.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
install_command_regex = re.compile(
r"<ConfigureCommand>(.+)</ConfigureCommand>", re.MULTILINE | re.DOTALL
)
m = install_command_regex.search(content)
assert m
install_command = m.group(1)
assert "pkg-a@" in install_command
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("a_Configure.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
install_command_regex = re.compile(
r"<ConfigureCommand>(.+)</ConfigureCommand>", re.MULTILINE | re.DOTALL
)
m = install_command_regex.search(content)
assert m
install_command = m.group(1)
assert "a@" in install_command
@pytest.mark.disable_clean_stage_check
@@ -774,15 +795,15 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock
# ^libdwarf
# ^mpich
# libelf@0.8.10
# pkg-a~bvv
# ^pkg-b
# pkg-a
# ^pkg-b
# a~bvv
# ^b
# a
# ^b
e = ev.create("test", with_view=False)
e.add("mpileaks")
e.add("libelf@0.8.10") # so env has both root and dep libelf specs
e.add("pkg-a")
e.add("pkg-a ~bvv")
e.add("a")
e.add("a ~bvv")
e.concretize()
e.write()
env_specs = e.all_specs()
@@ -793,9 +814,9 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock
# First find and remember some target concrete specs in the environment
for e_spec in env_specs:
if e_spec.satisfies(Spec("pkg-a ~bvv")):
if e_spec.satisfies(Spec("a ~bvv")):
a_spec = e_spec
elif e_spec.name == "pkg-b":
elif e_spec.name == "b":
b_spec = e_spec
elif e_spec.satisfies(Spec("mpi")):
mpi_spec = e_spec
@@ -818,8 +839,8 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock
assert "You can add specs to the environment with 'spack add " in inst_out
# Without --add, ensure that two packages "a" get installed
inst_out = install("pkg-a", output=str)
assert len([x for x in e.all_specs() if x.installed and x.name == "pkg-a"]) == 2
inst_out = install("a", output=str)
assert len([x for x in e.all_specs() if x.installed and x.name == "a"]) == 2
# Install an unambiguous dependency spec (that already exists as a dep
# in the environment) and make sure it gets installed (w/ deps),
@@ -852,7 +873,7 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock
# root of the environment as well as installed.
assert b_spec not in e.roots()
install("--add", "pkg-b")
install("--add", "b")
assert b_spec in e.roots()
assert b_spec not in e.uninstalled_specs()
@@ -887,7 +908,7 @@ def test_cdash_auth_token(tmpdir, mock_fetch, install_mockery, monkeypatch, capf
# capfd interferes with Spack's capturing
with tmpdir.as_cwd(), capfd.disabled():
monkeypatch.setenv("SPACK_CDASH_AUTH_TOKEN", "asdf")
out = install("-v", "--log-file=cdash_reports", "--log-format=cdash", "pkg-a")
out = install("-v", "--log-file=cdash_reports", "--log-format=cdash", "a")
assert "Using CDash auth token from environment" in out
@@ -895,42 +916,54 @@ def test_cdash_auth_token(tmpdir, mock_fetch, install_mockery, monkeypatch, capf
@pytest.mark.disable_clean_stage_check
def test_cdash_configure_warning(tmpdir, mock_fetch, install_mockery, capfd):
# capfd interferes with Spack's capturing of e.g., Build.xml output
with capfd.disabled(), tmpdir.as_cwd():
# Test would fail if install raised an error.
with capfd.disabled():
with tmpdir.as_cwd():
# Test would fail if install raised an error.
# Ensure that even on non-x86_64 architectures, there are no
# dependencies installed
spec = Spec("configure-warning").concretized()
spec.clear_dependencies()
specfile = "./spec.json"
with open(specfile, "w") as f:
f.write(spec.to_json())
# Ensure that even on non-x86_64 architectures, there are no
# dependencies installed
spec = spack.spec.Spec("configure-warning").concretized()
spec.clear_dependencies()
specfile = "./spec.json"
with open(specfile, "w") as f:
f.write(spec.to_json())
install("--log-file=cdash_reports", "--log-format=cdash", specfile)
# Verify Configure.xml exists with expected contents.
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("Configure.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert "foo: No such file or directory" in content
install("--log-file=cdash_reports", "--log-format=cdash", specfile)
# Verify Configure.xml exists with expected contents.
report_dir = tmpdir.join("cdash_reports")
assert report_dir in tmpdir.listdir()
report_file = report_dir.join("Configure.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert "foo: No such file or directory" in content
@pytest.mark.not_on_windows("ArchSpec gives test platform debian rather than windows")
def test_compiler_bootstrap(
install_mockery, mock_packages, mock_fetch, mock_archive, mutable_config, monkeypatch
install_mockery_mutable_config,
mock_packages,
mock_fetch,
mock_archive,
mutable_config,
monkeypatch,
):
monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False)
spack.config.set("config:install_missing_compilers", True)
assert CompilerSpec("gcc@=12.0") not in compilers.all_compiler_specs()
# Test succeeds if it does not raise an error
install("pkg-a%gcc@=12.0")
install("a%gcc@=12.0")
@pytest.mark.not_on_windows("Binary mirrors not supported on windows")
def test_compiler_bootstrap_from_binary_mirror(
install_mockery, mock_packages, mock_fetch, mock_archive, mutable_config, monkeypatch, tmpdir
install_mockery_mutable_config,
mock_packages,
mock_fetch,
mock_archive,
mutable_config,
monkeypatch,
tmpdir,
):
"""
Make sure installing compiler from buildcache registers compiler
@@ -959,14 +992,19 @@ def test_compiler_bootstrap_from_binary_mirror(
# Now make sure that when the compiler is installed from binary mirror,
# it also gets configured as a compiler. Test succeeds if it does not
# raise an error
install("--no-check-signature", "--cache-only", "--only", "dependencies", "pkg-b%gcc@=10.2.0")
install("--no-cache", "--only", "package", "pkg-b%gcc@10.2.0")
install("--no-check-signature", "--cache-only", "--only", "dependencies", "b%gcc@=10.2.0")
install("--no-cache", "--only", "package", "b%gcc@10.2.0")
@pytest.mark.not_on_windows("ArchSpec gives test platform debian rather than windows")
@pytest.mark.regression("16221")
def test_compiler_bootstrap_already_installed(
install_mockery, mock_packages, mock_fetch, mock_archive, mutable_config, monkeypatch
install_mockery_mutable_config,
mock_packages,
mock_fetch,
mock_archive,
mutable_config,
monkeypatch,
):
monkeypatch.setattr(spack.concretize.Concretizer, "check_for_compiler_existence", False)
spack.config.set("config:install_missing_compilers", True)
@@ -975,7 +1013,7 @@ def test_compiler_bootstrap_already_installed(
# Test succeeds if it does not raise an error
install("gcc@=12.0")
install("pkg-a%gcc@=12.0")
install("a%gcc@=12.0")
def test_install_fails_no_args(tmpdir):
@@ -1066,7 +1104,13 @@ def test_installation_fail_tests(install_mockery, mock_fetch, name, method):
@pytest.mark.not_on_windows("Buildcache not supported on windows")
def test_install_use_buildcache(
capsys, mock_packages, mock_fetch, mock_archive, mock_binary_index, tmpdir, install_mockery
capsys,
mock_packages,
mock_fetch,
mock_archive,
mock_binary_index,
tmpdir,
install_mockery_mutable_config,
):
"""
Make sure installing with use-buildcache behaves correctly.
@@ -1139,19 +1183,19 @@ def install_use_buildcache(opt):
@pytest.mark.not_on_windows("Windows logger I/O operation on closed file when install fails")
@pytest.mark.regression("34006")
@pytest.mark.disable_clean_stage_check
def test_padded_install_runtests_root(install_mockery, mock_fetch):
def test_padded_install_runtests_root(install_mockery_mutable_config, mock_fetch):
spack.config.set("config:install_tree:padded_length", 255)
output = install("--test=root", "--no-cache", "test-build-callbacks", fail_on_error=False)
assert output.count("method not implemented") == 1
@pytest.mark.regression("35337")
def test_report_filename_for_cdash(install_mockery, mock_fetch):
def test_report_filename_for_cdash(install_mockery_mutable_config, mock_fetch):
"""Test that the temporary file used to write the XML for CDash is not the upload URL"""
parser = argparse.ArgumentParser()
spack.cmd.install.setup_parser(parser)
args = parser.parse_args(
["--cdash-upload-url", "https://blahblah/submit.php?project=debugging", "pkg-a"]
["--cdash-upload-url", "https://blahblah/submit.php?project=debugging", "a"]
)
specs = spack.cmd.install.concrete_specs_from_cli(args, {})
filename = spack.cmd.install.report_filename(args, specs)

View File

@@ -3,6 +3,7 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import shutil
import pytest
@@ -15,8 +16,10 @@
from spack.main import SpackCommand, SpackCommandError
# Everything here uses (or can use) the mock config and database.
pytestmark = [pytest.mark.usefixtures("mutable_config", "mutable_database")]
pytestmark = [
pytest.mark.usefixtures("config", "database"),
pytest.mark.not_on_windows("does not run on windows"),
]
# location prints out "locations of packages and spack directories"
location = SpackCommand("location")
env = SpackCommand("env")
@@ -62,7 +65,7 @@ def test_location_source_dir_missing():
prefix = "==> Error: "
expected = (
"%sSource directory does not exist yet. Run this to create it:"
"%s spack stage %s" % (prefix, "\n", spec)
"%s spack stage %s" % (prefix, os.linesep, spec)
)
out = location("--source-dir", spec, fail_on_error=False).strip()
assert out == expected
@@ -123,7 +126,6 @@ def test_location_env_missing():
@pytest.mark.db
@pytest.mark.not_on_windows("Broken on Windows")
def test_location_install_dir(mock_spec):
"""Tests spack location --install-dir."""
spec, _ = mock_spec

View File

@@ -121,7 +121,7 @@ def test_maintainers_list_packages(mock_packages, capfd):
def test_maintainers_list_fails(mock_packages, capfd):
out = maintainers("pkg-a", fail_on_error=False)
out = maintainers("a", fail_on_error=False)
assert not out
assert maintainers.returncode == 1

View File

@@ -35,7 +35,7 @@ def test_regression_8083(tmpdir, capfd, mock_packages, mock_fetch, config):
@pytest.mark.regression("12345")
def test_mirror_from_env(tmp_path, mock_packages, mock_fetch, mutable_mock_env_path):
def test_mirror_from_env(tmp_path, mock_packages, mock_fetch, config, mutable_mock_env_path):
mirror_dir = str(tmp_path / "mirror")
env_name = "test"
@@ -232,7 +232,13 @@ def test_mirror_name_collision(mutable_config):
def test_mirror_destroy(
install_mockery, mock_packages, mock_fetch, mock_archive, mutable_config, monkeypatch, tmpdir
install_mockery_mutable_config,
mock_packages,
mock_fetch,
mock_archive,
mutable_config,
monkeypatch,
tmpdir,
):
# Create a temp mirror directory for buildcache usage
mirror_dir = tmpdir.join("mirror_dir")

View File

@@ -11,7 +11,6 @@
import spack.config
import spack.main
import spack.modules
import spack.spec
import spack.store
module = spack.main.SpackCommand("module")
@@ -140,7 +139,7 @@ def test_find_recursive():
@pytest.mark.db
def test_find_recursive_excluded(mutable_database, module_configuration):
def test_find_recursive_excluded(database, module_configuration):
module_configuration("exclude")
module("lmod", "refresh", "-y", "--delete-tree")
@@ -148,7 +147,7 @@ def test_find_recursive_excluded(mutable_database, module_configuration):
@pytest.mark.db
def test_loads_recursive_excluded(mutable_database, module_configuration):
def test_loads_recursive_excluded(database, module_configuration):
module_configuration("exclude")
module("lmod", "refresh", "-y", "--delete-tree")
@@ -179,8 +178,8 @@ def test_setdefault_command(mutable_database, mutable_config):
}
}
spack.config.set("modules", data)
# Install two different versions of pkg-a
other_spec, preferred = "pkg-a@1.0", "pkg-a@2.0"
# Install two different versions of a package
other_spec, preferred = "a@1.0", "a@2.0"
spack.spec.Spec(other_spec).concretized().package.do_install(fake=True)
spack.spec.Spec(preferred).concretized().package.do_install(fake=True)

View File

@@ -13,7 +13,6 @@
import spack.cmd.pkg
import spack.main
import spack.repo
import spack.util.file_cache
#: new fake package template
pkg_template = """\
@@ -29,20 +28,19 @@ def install(self, spec, prefix):
pass
"""
abc = {"mockpkg-a", "mockpkg-b", "mockpkg-c"}
abd = {"mockpkg-a", "mockpkg-b", "mockpkg-d"}
abc = set(("pkg-a", "pkg-b", "pkg-c"))
abd = set(("pkg-a", "pkg-b", "pkg-d"))
# Force all tests to use a git repository *in* the mock packages repo.
@pytest.fixture(scope="module")
def mock_pkg_git_repo(git, tmp_path_factory):
def mock_pkg_git_repo(git, tmpdir_factory):
"""Copy the builtin.mock repo and make a mutable git repo inside it."""
root_dir = tmp_path_factory.mktemp("mock_pkg_git_repo")
repo_dir = root_dir / "builtin.mock"
shutil.copytree(spack.paths.mock_packages_path, str(repo_dir))
tmproot = tmpdir_factory.mktemp("mock_pkg_git_repo")
repo_path = tmproot.join("builtin.mock")
repo_cache = spack.util.file_cache.FileCache(str(root_dir / "cache"))
mock_repo = spack.repo.RepoPath(str(repo_dir), cache=repo_cache)
shutil.copytree(spack.paths.mock_packages_path, str(repo_path))
mock_repo = spack.repo.RepoPath(str(repo_path))
mock_repo_packages = mock_repo.repos[0].packages_path
with working_dir(mock_repo_packages):
@@ -55,35 +53,29 @@ def mock_pkg_git_repo(git, tmp_path_factory):
git("config", "user.name", "Spack Testing")
git("-c", "commit.gpgsign=false", "commit", "-m", "initial mock repo commit")
# add commit with mockpkg-a, mockpkg-b, mockpkg-c packages
mkdirp("mockpkg-a", "mockpkg-b", "mockpkg-c")
with open("mockpkg-a/package.py", "w") as f:
# add commit with pkg-a, pkg-b, pkg-c packages
mkdirp("pkg-a", "pkg-b", "pkg-c")
with open("pkg-a/package.py", "w") as f:
f.write(pkg_template.format(name="PkgA"))
with open("mockpkg-b/package.py", "w") as f:
with open("pkg-b/package.py", "w") as f:
f.write(pkg_template.format(name="PkgB"))
with open("mockpkg-c/package.py", "w") as f:
with open("pkg-c/package.py", "w") as f:
f.write(pkg_template.format(name="PkgC"))
git("add", "mockpkg-a", "mockpkg-b", "mockpkg-c")
git("-c", "commit.gpgsign=false", "commit", "-m", "add mockpkg-a, mockpkg-b, mockpkg-c")
git("add", "pkg-a", "pkg-b", "pkg-c")
git("-c", "commit.gpgsign=false", "commit", "-m", "add pkg-a, pkg-b, pkg-c")
# remove mockpkg-c, add mockpkg-d
with open("mockpkg-b/package.py", "a") as f:
f.write("\n# change mockpkg-b")
git("add", "mockpkg-b")
mkdirp("mockpkg-d")
with open("mockpkg-d/package.py", "w") as f:
# remove pkg-c, add pkg-d
with open("pkg-b/package.py", "a") as f:
f.write("\n# change pkg-b")
git("add", "pkg-b")
mkdirp("pkg-d")
with open("pkg-d/package.py", "w") as f:
f.write(pkg_template.format(name="PkgD"))
git("add", "mockpkg-d")
git("rm", "-rf", "mockpkg-c")
git(
"-c",
"commit.gpgsign=false",
"commit",
"-m",
"change mockpkg-b, remove mockpkg-c, add mockpkg-d",
)
git("add", "pkg-d")
git("rm", "-rf", "pkg-c")
git("-c", "commit.gpgsign=false", "commit", "-m", "change pkg-b, remove pkg-c, add pkg-d")
with spack.repo.use_repositories(str(repo_dir)):
with spack.repo.use_repositories(str(repo_path)):
yield mock_repo_packages
@@ -94,11 +86,12 @@ def mock_pkg_names():
# Be sure to include virtual packages since packages with stand-alone
# tests may inherit additional tests from the virtuals they provide,
# such as packages that implement `mpi`.
return {
names = set(
name
for name in repo.all_package_names(include_virtuals=True)
if not name.startswith("mockpkg-")
}
if not name.startswith("pkg-")
)
return names
def split(output):
@@ -120,17 +113,17 @@ def test_mock_packages_path(mock_packages):
def test_pkg_add(git, mock_pkg_git_repo):
with working_dir(mock_pkg_git_repo):
mkdirp("mockpkg-e")
with open("mockpkg-e/package.py", "w") as f:
mkdirp("pkg-e")
with open("pkg-e/package.py", "w") as f:
f.write(pkg_template.format(name="PkgE"))
pkg("add", "mockpkg-e")
pkg("add", "pkg-e")
with working_dir(mock_pkg_git_repo):
try:
assert "A mockpkg-e/package.py" in git("status", "--short", output=str)
assert "A pkg-e/package.py" in git("status", "--short", output=str)
finally:
shutil.rmtree("mockpkg-e")
shutil.rmtree("pkg-e")
# Removing a package mid-run disrupts Spack's caching
if spack.repo.PATH.repos[0]._fast_package_checker:
spack.repo.PATH.repos[0]._fast_package_checker.invalidate()
@@ -145,10 +138,10 @@ def test_pkg_list(mock_pkg_git_repo, mock_pkg_names):
assert sorted(mock_pkg_names) == sorted(out)
out = split(pkg("list", "HEAD^"))
assert sorted(mock_pkg_names.union(["mockpkg-a", "mockpkg-b", "mockpkg-c"])) == sorted(out)
assert sorted(mock_pkg_names.union(["pkg-a", "pkg-b", "pkg-c"])) == sorted(out)
out = split(pkg("list", "HEAD"))
assert sorted(mock_pkg_names.union(["mockpkg-a", "mockpkg-b", "mockpkg-d"])) == sorted(out)
assert sorted(mock_pkg_names.union(["pkg-a", "pkg-b", "pkg-d"])) == sorted(out)
# test with three dots to make sure pkg calls `git merge-base`
out = split(pkg("list", "HEAD^^..."))
@@ -158,25 +151,25 @@ def test_pkg_list(mock_pkg_git_repo, mock_pkg_names):
@pytest.mark.not_on_windows("stdout format conflict")
def test_pkg_diff(mock_pkg_git_repo, mock_pkg_names):
out = split(pkg("diff", "HEAD^^", "HEAD^"))
assert out == ["HEAD^:", "mockpkg-a", "mockpkg-b", "mockpkg-c"]
assert out == ["HEAD^:", "pkg-a", "pkg-b", "pkg-c"]
out = split(pkg("diff", "HEAD^^", "HEAD"))
assert out == ["HEAD:", "mockpkg-a", "mockpkg-b", "mockpkg-d"]
assert out == ["HEAD:", "pkg-a", "pkg-b", "pkg-d"]
out = split(pkg("diff", "HEAD^", "HEAD"))
assert out == ["HEAD^:", "mockpkg-c", "HEAD:", "mockpkg-d"]
assert out == ["HEAD^:", "pkg-c", "HEAD:", "pkg-d"]
@pytest.mark.not_on_windows("stdout format conflict")
def test_pkg_added(mock_pkg_git_repo):
out = split(pkg("added", "HEAD^^", "HEAD^"))
assert ["mockpkg-a", "mockpkg-b", "mockpkg-c"] == out
assert ["pkg-a", "pkg-b", "pkg-c"] == out
out = split(pkg("added", "HEAD^^", "HEAD"))
assert ["mockpkg-a", "mockpkg-b", "mockpkg-d"] == out
assert ["pkg-a", "pkg-b", "pkg-d"] == out
out = split(pkg("added", "HEAD^", "HEAD"))
assert ["mockpkg-d"] == out
assert ["pkg-d"] == out
out = split(pkg("added", "HEAD", "HEAD"))
assert out == []
@@ -191,7 +184,7 @@ def test_pkg_removed(mock_pkg_git_repo):
assert out == []
out = split(pkg("removed", "HEAD^", "HEAD"))
assert out == ["mockpkg-c"]
assert out == ["pkg-c"]
@pytest.mark.not_on_windows("stdout format conflict")
@@ -203,34 +196,34 @@ def test_pkg_changed(mock_pkg_git_repo):
assert out == []
out = split(pkg("changed", "--type", "a", "HEAD^^", "HEAD^"))
assert out == ["mockpkg-a", "mockpkg-b", "mockpkg-c"]
assert out == ["pkg-a", "pkg-b", "pkg-c"]
out = split(pkg("changed", "--type", "r", "HEAD^^", "HEAD^"))
assert out == []
out = split(pkg("changed", "--type", "ar", "HEAD^^", "HEAD^"))
assert out == ["mockpkg-a", "mockpkg-b", "mockpkg-c"]
assert out == ["pkg-a", "pkg-b", "pkg-c"]
out = split(pkg("changed", "--type", "arc", "HEAD^^", "HEAD^"))
assert out == ["mockpkg-a", "mockpkg-b", "mockpkg-c"]
assert out == ["pkg-a", "pkg-b", "pkg-c"]
out = split(pkg("changed", "HEAD^", "HEAD"))
assert out == ["mockpkg-b"]
assert out == ["pkg-b"]
out = split(pkg("changed", "--type", "c", "HEAD^", "HEAD"))
assert out == ["mockpkg-b"]
assert out == ["pkg-b"]
out = split(pkg("changed", "--type", "a", "HEAD^", "HEAD"))
assert out == ["mockpkg-d"]
assert out == ["pkg-d"]
out = split(pkg("changed", "--type", "r", "HEAD^", "HEAD"))
assert out == ["mockpkg-c"]
assert out == ["pkg-c"]
out = split(pkg("changed", "--type", "ar", "HEAD^", "HEAD"))
assert out == ["mockpkg-c", "mockpkg-d"]
assert out == ["pkg-c", "pkg-d"]
out = split(pkg("changed", "--type", "arc", "HEAD^", "HEAD"))
assert out == ["mockpkg-b", "mockpkg-c", "mockpkg-d"]
assert out == ["pkg-b", "pkg-c", "pkg-d"]
# invalid type argument
with pytest.raises(spack.main.SpackCommandError):
@@ -296,7 +289,7 @@ def test_pkg_canonical_source(mock_packages):
def test_pkg_hash(mock_packages):
output = pkg("hash", "pkg-a", "pkg-b").strip().split()
output = pkg("hash", "a", "b").strip().split()
assert len(output) == 2 and all(len(elt) == 32 for elt in output)
output = pkg("hash", "multimethod").strip().split()

View File

@@ -10,6 +10,8 @@
providers = SpackCommand("providers")
pytestmark = pytest.mark.not_on_windows("Providers not currently supported on Windows")
@pytest.mark.parametrize(
"pkg",

View File

@@ -4,6 +4,8 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import pytest
import spack.store
from spack.main import SpackCommand
@@ -11,6 +13,8 @@
deprecate = SpackCommand("deprecate")
reindex = SpackCommand("reindex")
pytestmark = pytest.mark.not_on_windows("does not run on windows")
def test_reindex_basic(mock_packages, mock_archive, mock_fetch, install_mockery):
install("libelf@0.8.13")

View File

@@ -14,7 +14,7 @@
import spack.store
from spack.main import SpackCommand, SpackCommandError
pytestmark = pytest.mark.usefixtures("mutable_config", "mutable_mock_repo")
pytestmark = pytest.mark.usefixtures("config", "mutable_mock_repo")
spec = SpackCommand("spec")
@@ -31,7 +31,7 @@ def test_spec():
@pytest.mark.only_clingo("Known failure of the original concretizer")
def test_spec_concretizer_args(mutable_database, do_not_check_runtimes_on_reuse):
def test_spec_concretizer_args(mutable_config, mutable_database, do_not_check_runtimes_on_reuse):
"""End-to-end test of CLI concretizer prefs.
It's here to make sure that everything works from CLI
@@ -58,7 +58,7 @@ def test_spec_concretizer_args(mutable_database, do_not_check_runtimes_on_reuse)
def test_spec_parse_dependency_variant_value():
"""Verify that we can provide multiple key=value variants to multiple separate
packages within a spec string."""
output = spec("multivalue-variant fee=barbaz ^ pkg-a foobar=baz")
output = spec("multivalue-variant fee=barbaz ^ a foobar=baz")
assert "fee=barbaz" in output
assert "foobar=baz" in output
@@ -97,7 +97,7 @@ def test_spec_json():
assert "mpich" in mpileaks
def test_spec_format(mutable_database):
def test_spec_format(database, config):
output = spec("--format", "{name}-{^mpi.name}", "mpileaks^mpich")
assert output.rstrip("\n") == "mpileaks-mpich"

View File

@@ -22,6 +22,7 @@
pytestmark = pytest.mark.usefixtures("install_mockery", "mock_packages")
@pytest.mark.not_on_windows("not implemented on windows")
@pytest.mark.disable_clean_stage_check
def test_stage_spec(monkeypatch):
"""Verify that staging specs works."""
@@ -62,6 +63,7 @@ def test_stage_path_errors_multiple_specs(check_stage_path):
stage(f"--path={check_stage_path}", "trivial-install-test-package", "mpileaks")
@pytest.mark.not_on_windows("not implemented on windows")
@pytest.mark.disable_clean_stage_check
def test_stage_with_env_outside_env(mutable_mock_env_path, monkeypatch):
"""Verify that stage concretizes specs not in environment instead of erroring."""
@@ -80,6 +82,7 @@ def fake_stage(pkg, mirror_only=False):
stage("trivial-install-test-package")
@pytest.mark.not_on_windows("not implemented on windows")
@pytest.mark.disable_clean_stage_check
def test_stage_with_env_inside_env(mutable_mock_env_path, monkeypatch):
"""Verify that stage filters specs in environment instead of reconcretizing."""
@@ -98,6 +101,7 @@ def fake_stage(pkg, mirror_only=False):
stage("mpileaks")
@pytest.mark.not_on_windows("not implemented on windows")
@pytest.mark.disable_clean_stage_check
def test_stage_full_env(mutable_mock_env_path, monkeypatch):
"""Verify that stage filters specs in environment."""

View File

@@ -38,7 +38,7 @@ def flake8_package(tmpdir):
change to the ``flake8`` mock package, yields the filename, then undoes the
change on cleanup.
"""
repo = spack.repo.from_path(spack.paths.mock_packages_path)
repo = spack.repo.Repo(spack.paths.mock_packages_path)
filename = repo.filename_for_package_name("flake8")
rel_path = os.path.dirname(os.path.relpath(filename, spack.paths.prefix))
tmp = tmpdir / rel_path / "flake8-ci-package.py"
@@ -54,7 +54,7 @@ def flake8_package(tmpdir):
@pytest.fixture
def flake8_package_with_errors(scope="function"):
"""A flake8 package with errors."""
repo = spack.repo.from_path(spack.paths.mock_packages_path)
repo = spack.repo.Repo(spack.paths.mock_packages_path)
filename = repo.filename_for_package_name("flake8")
tmp = filename + ".tmp"
@@ -130,7 +130,7 @@ def test_changed_files_all_files():
assert os.path.join(spack.paths.module_path, "spec.py") in files
# a mock package
repo = spack.repo.from_path(spack.paths.mock_packages_path)
repo = spack.repo.Repo(spack.paths.mock_packages_path)
filename = repo.filename_for_package_name("flake8")
assert filename in files

View File

@@ -10,14 +10,10 @@
from llnl.util.filesystem import copy_tree
import spack.cmd.common.arguments
import spack.cmd.install
import spack.cmd.test
import spack.config
import spack.install_test
import spack.package_base
import spack.paths
import spack.spec
import spack.store
from spack.install_test import TestStatus
from spack.main import SpackCommand
@@ -29,7 +25,13 @@
def test_test_package_not_installed(
tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery, mock_test_stage
tmpdir,
mock_packages,
mock_archive,
mock_fetch,
config,
install_mockery_mutable_config,
mock_test_stage,
):
output = spack_test("run", "libdwarf")
@@ -52,7 +54,7 @@ def test_test_dirty_flag(arguments, expected):
def test_test_dup_alias(
mock_test_stage, mock_packages, mock_archive, mock_fetch, install_mockery, capfd
mock_test_stage, mock_packages, mock_archive, mock_fetch, install_mockery_mutable_config, capfd
):
"""Ensure re-using an alias fails with suggestion to change."""
install("libdwarf")
@@ -67,7 +69,9 @@ def test_test_dup_alias(
assert "already exists" in out and "Try another alias" in out
def test_test_output(mock_test_stage, mock_packages, mock_archive, mock_fetch, install_mockery):
def test_test_output(
mock_test_stage, mock_packages, mock_archive, mock_fetch, install_mockery_mutable_config
):
"""Ensure output printed from pkgs is captured by output redirection."""
install("printing-package")
spack_test("run", "--alias", "printpkg", "printing-package")
@@ -93,7 +97,13 @@ def test_test_output(mock_test_stage, mock_packages, mock_archive, mock_fetch, i
"pkg_name,failure", [("test-error", "exited with status 1"), ("test-fail", "not callable")]
)
def test_test_output_fails(
mock_packages, mock_archive, mock_fetch, install_mockery, mock_test_stage, pkg_name, failure
mock_packages,
mock_archive,
mock_fetch,
install_mockery_mutable_config,
mock_test_stage,
pkg_name,
failure,
):
"""Confirm stand-alone test failure with expected outputs."""
install(pkg_name)
@@ -107,7 +117,9 @@ def test_test_output_fails(
assert "See test log for details" in out
@pytest.mark.usefixtures("mock_packages", "mock_archive", "mock_fetch", "install_mockery")
@pytest.mark.usefixtures(
"mock_packages", "mock_archive", "mock_fetch", "install_mockery_mutable_config"
)
@pytest.mark.parametrize(
"pkg_name,msgs",
[
@@ -141,7 +153,13 @@ def test_junit_output_with_failures(tmpdir, mock_test_stage, pkg_name, msgs):
def test_cdash_output_test_error(
tmpdir, mock_fetch, install_mockery, mock_packages, mock_archive, mock_test_stage, capfd
tmpdir,
mock_fetch,
install_mockery_mutable_config,
mock_packages,
mock_archive,
mock_test_stage,
capfd,
):
"""Confirm stand-alone test error expected outputs in CDash reporting."""
install("test-error")
@@ -161,7 +179,12 @@ def test_cdash_output_test_error(
def test_cdash_upload_clean_test(
tmpdir, mock_fetch, install_mockery, mock_packages, mock_archive, mock_test_stage
tmpdir,
mock_fetch,
install_mockery_mutable_config,
mock_packages,
mock_archive,
mock_test_stage,
):
install("printing-package")
with tmpdir.as_cwd():
@@ -210,7 +233,7 @@ def test_test_list_all(mock_packages):
)
def test_test_list(mock_packages, mock_archive, mock_fetch, install_mockery):
def test_test_list(mock_packages, mock_archive, mock_fetch, install_mockery_mutable_config):
pkg_with_tests = "printing-package"
install(pkg_with_tests)
output = spack_test("list")
@@ -276,7 +299,7 @@ def test_test_results_status(mock_packages, mock_test_stage, status):
@pytest.mark.regression("35337")
def test_report_filename_for_cdash(install_mockery, mock_fetch):
def test_report_filename_for_cdash(install_mockery_mutable_config, mock_fetch):
"""Test that the temporary file used to write Testing.xml for CDash is not the upload URL"""
name = "trivial"
spec = spack.spec.Spec("trivial-smoke-test").concretized()
@@ -299,7 +322,7 @@ def test_report_filename_for_cdash(install_mockery, mock_fetch):
def test_test_output_multiple_specs(
mock_test_stage, mock_packages, mock_archive, mock_fetch, install_mockery
mock_test_stage, mock_packages, mock_archive, mock_fetch, install_mockery_mutable_config
):
"""Ensure proper reporting for suite with skipped, failing, and passed tests."""
install("test-error", "simple-standalone-test@0.9", "simple-standalone-test@1.0")

View File

@@ -222,7 +222,7 @@ class TestUninstallFromEnv:
@pytest.fixture(scope="function")
def environment_setup(
self, mutable_mock_env_path, mock_packages, mutable_database, install_mockery
self, mutable_mock_env_path, config, mock_packages, mutable_database, install_mockery
):
TestUninstallFromEnv.env("create", "e1")
e1 = spack.environment.read("e1")

View File

@@ -3,6 +3,7 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import re
import sys
import pytest
@@ -116,6 +117,7 @@ def test_url_summary(mock_packages):
assert out_correct_versions == correct_versions
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Unsupported on Windows for now")
def test_url_stats(capfd, mock_packages):
with capfd.disabled():
output = url("stats")

View File

@@ -63,7 +63,9 @@ def test_single_file_verify_cmd(tmpdir):
assert sorted(errors) == sorted(expected)
def test_single_spec_verify_cmd(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery):
def test_single_spec_verify_cmd(
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery
):
# Test the verify command interface to verify a single spec
install("libelf")
s = spack.spec.Spec("libelf").concretized()

View File

@@ -28,7 +28,9 @@ def create_projection_file(tmpdir, projection):
@pytest.mark.parametrize("cmd", ["hardlink", "symlink", "hard", "add", "copy", "relocate"])
def test_view_link_type(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery, cmd):
def test_view_link_type(
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery, cmd
):
install("libdwarf")
viewpath = str(tmpdir.mkdir("view_{0}".format(cmd)))
view(cmd, viewpath, "libdwarf")
@@ -42,7 +44,7 @@ def test_view_link_type(tmpdir, mock_packages, mock_archive, mock_fetch, install
@pytest.mark.parametrize("add_cmd", ["hardlink", "symlink", "hard", "add", "copy", "relocate"])
def test_view_link_type_remove(
tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery, add_cmd
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery, add_cmd
):
install("needs-relocation")
viewpath = str(tmpdir.mkdir("view_{0}".format(add_cmd)))
@@ -55,7 +57,9 @@ def test_view_link_type_remove(
@pytest.mark.parametrize("cmd", ["hardlink", "symlink", "hard", "add", "copy", "relocate"])
def test_view_projections(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery, cmd):
def test_view_projections(
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery, cmd
):
install("libdwarf@20130207")
viewpath = str(tmpdir.mkdir("view_{0}".format(cmd)))
@@ -72,7 +76,7 @@ def test_view_projections(tmpdir, mock_packages, mock_archive, mock_fetch, insta
def test_view_multiple_projections(
tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery
):
install("libdwarf@20130207")
install("extendee@1.0%gcc")
@@ -92,7 +96,7 @@ def test_view_multiple_projections(
def test_view_multiple_projections_all_first(
tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery
):
install("libdwarf@20130207")
install("extendee@1.0%gcc")
@@ -111,14 +115,14 @@ def test_view_multiple_projections_all_first(
assert os.path.exists(extendee_prefix)
def test_view_external(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery):
def test_view_external(tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery):
install("externaltool")
viewpath = str(tmpdir.mkdir("view"))
output = view("symlink", viewpath, "externaltool")
assert "Skipping external package: externaltool" in output
def test_view_extension(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery):
def test_view_extension(tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery):
install("extendee")
install("extension1@1.0")
install("extension1@2.0")
@@ -132,7 +136,9 @@ def test_view_extension(tmpdir, mock_packages, mock_archive, mock_fetch, install
assert os.path.exists(os.path.join(viewpath, "bin", "extension1"))
def test_view_extension_remove(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery):
def test_view_extension_remove(
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery
):
install("extendee")
install("extension1@1.0")
viewpath = str(tmpdir.mkdir("view"))
@@ -143,7 +149,9 @@ def test_view_extension_remove(tmpdir, mock_packages, mock_archive, mock_fetch,
assert not os.path.exists(os.path.join(viewpath, "bin", "extension1"))
def test_view_extension_conflict(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery):
def test_view_extension_conflict(
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery
):
install("extendee")
install("extension1@1.0")
install("extension1@2.0")
@@ -154,7 +162,7 @@ def test_view_extension_conflict(tmpdir, mock_packages, mock_archive, mock_fetch
def test_view_extension_conflict_ignored(
tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery
):
install("extendee")
install("extension1@1.0")
@@ -176,7 +184,7 @@ def test_view_fails_with_missing_projections_file(tmpdir):
@pytest.mark.parametrize("with_projection", [False, True])
@pytest.mark.parametrize("cmd", ["symlink", "copy"])
def test_view_files_not_ignored(
tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery, cmd, with_projection
tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery, cmd, with_projection
):
spec = Spec("view-not-ignored").concretized()
pkg = spec.package

View File

@@ -24,8 +24,6 @@
import spack.platforms
import spack.repo
import spack.solver.asp
import spack.store
import spack.util.file_cache
import spack.util.libc
import spack.variant as vt
from spack.concretize import find_spec
@@ -170,18 +168,19 @@ def reverser(pkg_name):
@pytest.fixture()
def repo_with_changing_recipe(tmp_path_factory, mutable_mock_repo):
def repo_with_changing_recipe(tmpdir_factory, mutable_mock_repo):
repo_namespace = "changing"
repo_dir = tmp_path_factory.mktemp(repo_namespace)
repo_dir = tmpdir_factory.mktemp(repo_namespace)
(repo_dir / "repo.yaml").write_text(
repo_dir.join("repo.yaml").write(
"""
repo:
namespace: changing
"""
""",
ensure=True,
)
packages_dir = repo_dir / "packages"
packages_dir = repo_dir.ensure("packages", dir=True)
root_pkg_str = """
class Root(Package):
homepage = "http://www.example.com"
@@ -192,9 +191,7 @@ class Root(Package):
conflicts("^changing~foo")
"""
package_py = packages_dir / "root" / "package.py"
package_py.parent.mkdir(parents=True)
package_py.write_text(root_pkg_str)
packages_dir.join("root", "package.py").write(root_pkg_str, ensure=True)
changing_template = """
class Changing(Package):
@@ -228,9 +225,7 @@ class _ChangingPackage:
def __init__(self, repo_directory):
self.repo_dir = repo_directory
cache_dir = tmp_path_factory.mktemp("cache")
self.repo_cache = spack.util.file_cache.FileCache(str(cache_dir))
self.repo = spack.repo.Repo(str(repo_directory), cache=self.repo_cache)
self.repo = spack.repo.Repo(str(repo_directory))
def change(self, changes=None):
changes = changes or {}
@@ -251,12 +246,10 @@ def change(self, changes=None):
# Change the recipe
t = jinja2.Template(changing_template)
changing_pkg_str = t.render(**context)
package_py = packages_dir / "changing" / "package.py"
package_py.parent.mkdir(parents=True, exist_ok=True)
package_py.write_text(changing_pkg_str)
packages_dir.join("changing", "package.py").write(changing_pkg_str, ensure=True)
# Re-add the repository
self.repo = spack.repo.Repo(str(self.repo_dir), cache=self.repo_cache)
self.repo = spack.repo.Repo(str(self.repo_dir))
repository.put_first(self.repo)
_changing_pkg = _ChangingPackage(repo_dir)
@@ -411,7 +404,7 @@ def test_compiler_flags_from_compiler_and_dependent(self):
def test_compiler_flags_differ_identical_compilers(self, mutable_config, clang12_with_flags):
mutable_config.set("compilers", [clang12_with_flags])
# Correct arch to use test compiler that has flags
spec = Spec("pkg-a %clang@12.2.0 platform=test os=fe target=fe")
spec = Spec("a %clang@12.2.0 platform=test os=fe target=fe")
# Get the compiler that matches the spec (
compiler = spack.compilers.compiler_for_spec("clang@=12.2.0", spec.architecture)
@@ -488,7 +481,7 @@ def test_architecture_deep_inheritance(self, mock_targets, compiler_factory):
assert s.architecture.target == spec.architecture.target
def test_compiler_flags_from_user_are_grouped(self):
spec = Spec('pkg-a%gcc cflags="-O -foo-flag foo-val" platform=test')
spec = Spec('a%gcc cflags="-O -foo-flag foo-val" platform=test')
spec.concretize()
cflags = spec.compiler_flags["cflags"]
assert any(x == "-foo-flag foo-val" for x in cflags)
@@ -596,20 +589,20 @@ def test_concretize_propagate_multivalue_variant(self):
spec = Spec("multivalue-variant foo==baz,fee")
spec.concretize()
assert spec.satisfies("^pkg-a foo=baz,fee")
assert spec.satisfies("^pkg-b foo=baz,fee")
assert not spec.satisfies("^pkg-a foo=bar")
assert not spec.satisfies("^pkg-b foo=bar")
assert spec.satisfies("^a foo=baz,fee")
assert spec.satisfies("^b foo=baz,fee")
assert not spec.satisfies("^a foo=bar")
assert not spec.satisfies("^b foo=bar")
def test_no_matching_compiler_specs(self, mock_low_high_config):
# only relevant when not building compilers as needed
with spack.concretize.enable_compiler_existence_check():
s = Spec("pkg-a %gcc@=0.0.0")
s = Spec("a %gcc@=0.0.0")
with pytest.raises(spack.concretize.UnavailableCompilerVersionError):
s.concretize()
def test_no_compilers_for_arch(self):
s = Spec("pkg-a arch=linux-rhel0-x86_64")
s = Spec("a arch=linux-rhel0-x86_64")
with pytest.raises(spack.error.SpackError):
s.concretize()
@@ -689,8 +682,7 @@ def test_nobuild_package(self):
with pytest.raises(spack.error.SpecError):
spec.concretize()
def test_external_and_virtual(self, mutable_config):
mutable_config.set("packages:stuff", {"buildable": False})
def test_external_and_virtual(self):
spec = Spec("externaltest")
spec.concretize()
assert spec["externaltool"].external_path == os.path.sep + os.path.join(
@@ -819,7 +811,7 @@ def test_regression_issue_7941(self):
# The string representation of a spec containing
# an explicit multi-valued variant and a dependency
# might be parsed differently than the originating spec
s = Spec("pkg-a foobar=bar ^pkg-b")
s = Spec("a foobar=bar ^b")
t = Spec(str(s))
s.concretize()
@@ -1172,14 +1164,16 @@ def test_external_that_would_require_a_virtual_dependency(self):
assert s.external
assert "stuff" not in s
def test_transitive_conditional_virtual_dependency(self, mutable_config):
"""Test that an external is used as provider if the virtual is non-buildable"""
mutable_config.set("packages:stuff", {"buildable": False})
def test_transitive_conditional_virtual_dependency(self):
s = Spec("transitive-conditional-virtual-dependency").concretized()
# Test that the default +stuff~mpi is maintained, and the right provider is selected
assert s.satisfies("^conditional-virtual-dependency +stuff~mpi")
assert s.satisfies("^[virtuals=stuff] externalvirtual")
# The default for conditional-virtual-dependency is to have
# +stuff~mpi, so check that these defaults are respected
assert "+stuff" in s["conditional-virtual-dependency"]
assert "~mpi" in s["conditional-virtual-dependency"]
# 'stuff' is provided by an external package, so check it's present
assert "externalvirtual" in s
@pytest.mark.regression("20040")
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
@@ -1197,14 +1191,14 @@ def test_conditional_provides_or_depends_on(self):
[
# Check that True is treated correctly and attaches test deps
# to all nodes in the DAG
("pkg-a", True, ["pkg-a"], []),
("pkg-a foobar=bar", True, ["pkg-a", "pkg-b"], []),
("a", True, ["a"], []),
("a foobar=bar", True, ["a", "b"], []),
# Check that a list of names activates the dependency only for
# packages in that list
("pkg-a foobar=bar", ["pkg-a"], ["pkg-a"], ["pkg-b"]),
("pkg-a foobar=bar", ["pkg-b"], ["pkg-b"], ["pkg-a"]),
("a foobar=bar", ["a"], ["a"], ["b"]),
("a foobar=bar", ["b"], ["b"], ["a"]),
# Check that False disregard test dependencies
("pkg-a foobar=bar", False, [], ["pkg-a", "pkg-b"]),
("a foobar=bar", False, [], ["a", "b"]),
],
)
def test_activating_test_dependencies(self, spec_str, tests_arg, with_dep, without_dep):
@@ -1263,7 +1257,7 @@ def test_custom_compiler_version(self, mutable_config, compiler_factory, monkeyp
"compilers", [compiler_factory(spec="gcc@10foo", operating_system="redhat6")]
)
monkeypatch.setattr(spack.compiler.Compiler, "real_version", "10.2.1")
s = Spec("pkg-a %gcc@10foo os=redhat6").concretized()
s = Spec("a %gcc@10foo os=redhat6").concretized()
assert "%gcc@10foo" in s
def test_all_patches_applied(self):
@@ -1407,10 +1401,10 @@ def test_no_reuse_when_variant_condition_does_not_hold(self, mutable_database, m
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
def test_reuse_with_flags(self, mutable_database, mutable_config):
spack.config.set("concretizer:reuse", True)
spec = Spec("pkg-a cflags=-g cxxflags=-g").concretized()
spec = Spec("a cflags=-g cxxflags=-g").concretized()
spack.store.STORE.db.add(spec, None)
testspec = Spec("pkg-a cflags=-g")
testspec = Spec("a cflags=-g")
testspec.concretize()
assert testspec == spec
@@ -1593,7 +1587,7 @@ def test_non_default_provider_of_multiple_virtuals(self):
)
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
def test_concrete_specs_are_not_modified_on_reuse(
self, mutable_database, spec_str, expect_installed
self, mutable_database, spec_str, expect_installed, config
):
# Test the internal consistency of solve + DAG reconstruction
# when reused specs are added to the mix. This prevents things
@@ -1753,31 +1747,31 @@ def test_reuse_with_unknown_namespace_dont_raise(
self, temporary_store, mock_custom_repository
):
with spack.repo.use_repositories(mock_custom_repository, override=False):
s = Spec("pkg-c").concretized()
s = Spec("c").concretized()
assert s.namespace != "builtin.mock"
s.package.do_install(fake=True, explicit=True)
with spack.config.override("concretizer:reuse", True):
s = Spec("pkg-c").concretized()
s = Spec("c").concretized()
assert s.namespace == "builtin.mock"
@pytest.mark.regression("28259")
def test_reuse_with_unknown_package_dont_raise(self, tmpdir, temporary_store, monkeypatch):
builder = spack.repo.MockRepositoryBuilder(tmpdir.mkdir("mock.repo"), namespace="myrepo")
builder.add_package("pkg-c")
builder.add_package("c")
with spack.repo.use_repositories(builder.root, override=False):
s = Spec("pkg-c").concretized()
s = Spec("c").concretized()
assert s.namespace == "myrepo"
s.package.do_install(fake=True, explicit=True)
del sys.modules["spack.pkg.myrepo.pkg-c"]
del sys.modules["spack.pkg.myrepo.c"]
del sys.modules["spack.pkg.myrepo"]
builder.remove("pkg-c")
builder.remove("c")
with spack.repo.use_repositories(builder.root, override=False) as repos:
# TODO (INJECT CONFIGURATION): unclear why the cache needs to be invalidated explicitly
repos.repos[0]._pkg_checker.invalidate()
with spack.config.override("concretizer:reuse", True):
s = Spec("pkg-c").concretized()
s = Spec("c").concretized()
assert s.namespace == "builtin.mock"
@pytest.mark.parametrize(
@@ -1785,7 +1779,7 @@ def test_reuse_with_unknown_package_dont_raise(self, tmpdir, temporary_store, mo
[
(["libelf", "libelf@0.8.10"], 1, 1),
(["libdwarf%gcc", "libelf%clang"], 2, 1),
(["libdwarf%gcc", "libdwarf%clang"], 3, 1),
(["libdwarf%gcc", "libdwarf%clang"], 3, 2),
(["libdwarf^libelf@0.8.12", "libdwarf^libelf@0.8.13"], 4, 1),
(["hdf5", "zmpi"], 3, 1),
(["hdf5", "mpich"], 2, 1),
@@ -1856,7 +1850,7 @@ def test_best_effort_coconcretize_preferences(self, specs, expected_spec, occura
assert counter == occurances, concrete_specs
@pytest.mark.only_clingo("Original concretizer cannot concretize in rounds")
def test_solve_in_rounds_all_unsolved(self, monkeypatch, mock_packages):
def test_solve_in_rounds_all_unsolved(self, monkeypatch, mock_packages, config):
specs = [Spec(x) for x in ["libdwarf%gcc", "libdwarf%clang"]]
solver = spack.solver.asp.Solver()
solver.reuse = False
@@ -1908,20 +1902,20 @@ def test_misleading_error_message_on_version(self, mutable_database):
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
def test_version_weight_and_provenance(self):
"""Test package preferences during coconcretization."""
reusable_specs = [Spec(spec_str).concretized() for spec_str in ("pkg-b@0.9", "pkg-b@1.0")]
root_spec = Spec("pkg-a foobar=bar")
reusable_specs = [Spec(spec_str).concretized() for spec_str in ("b@0.9", "b@1.0")]
root_spec = Spec("a foobar=bar")
with spack.config.override("concretizer:reuse", True):
solver = spack.solver.asp.Solver()
setup = spack.solver.asp.SpackSolverSetup()
result, _, _ = solver.driver.solve(setup, [root_spec], reuse=reusable_specs)
# The result here should have a single spec to build ('pkg-a')
# and it should be using pkg-b@1.0 with a version badness of 2
# The result here should have a single spec to build ('a')
# and it should be using b@1.0 with a version badness of 2
# The provenance is:
# version_declared("pkg-b","1.0",0,"package_py").
# version_declared("pkg-b","0.9",1,"package_py").
# version_declared("pkg-b","1.0",2,"installed").
# version_declared("pkg-b","0.9",3,"installed").
# version_declared("b","1.0",0,"package_py").
# version_declared("b","0.9",1,"package_py").
# version_declared("b","1.0",2,"installed").
# version_declared("b","0.9",3,"installed").
#
# Depending on the target, it may also use gnuconfig
result_spec = result.specs[0]
@@ -1935,11 +1929,11 @@ def test_version_weight_and_provenance(self):
for criterion in criteria:
assert criterion in result.criteria, criterion
assert result_spec.satisfies("^pkg-b@1.0")
assert result_spec.satisfies("^b@1.0")
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
def test_reuse_succeeds_with_config_compatible_os(self):
root_spec = Spec("pkg-b")
root_spec = Spec("b")
s = root_spec.concretized()
other_os = s.copy()
mock_os = "ubuntu2204"
@@ -2203,7 +2197,7 @@ def test_external_python_extension_find_unified_python(self):
"specs",
[
["mpileaks^ callpath ^dyninst@8.1.1:8 ^mpich2@1.3:1"],
["multivalue-variant ^pkg-a@2:2"],
["multivalue-variant ^a@2:2"],
["v1-consumer ^conditional-provider@1:1 +disable-v1"],
],
)
@@ -2219,7 +2213,7 @@ def test_result_specs_is_not_empty(self, specs):
assert result.specs
@pytest.mark.regression("38664")
def test_unsolved_specs_raises_error(self, monkeypatch, mock_packages):
def test_unsolved_specs_raises_error(self, monkeypatch, mock_packages, config):
"""Check that the solver raises an exception when input specs are not
satisfied.
"""
@@ -2239,12 +2233,12 @@ def test_unsolved_specs_raises_error(self, monkeypatch, mock_packages):
@pytest.mark.regression("43141")
@pytest.mark.only_clingo("Use case not supported by the original concretizer")
def test_clear_error_when_unknown_compiler_requested(self, mock_packages):
def test_clear_error_when_unknown_compiler_requested(self, mock_packages, config):
"""Tests that the solver can report a case where the compiler cannot be set"""
with pytest.raises(
spack.error.UnsatisfiableSpecError, match="Cannot set the required compiler: pkg-a%foo"
spack.error.UnsatisfiableSpecError, match="Cannot set the required compiler: a%foo"
):
Spec("pkg-a %foo").concretized()
Spec("a %foo").concretized()
@pytest.mark.regression("36339")
def test_compiler_match_constraints_when_selected(self):
@@ -2280,7 +2274,7 @@ def test_compiler_match_constraints_when_selected(self):
},
]
spack.config.set("compilers", compiler_configuration)
s = Spec("pkg-a %gcc@:11").concretized()
s = Spec("a %gcc@:11").concretized()
assert s.compiler.version == ver("=11.1.0"), s
@pytest.mark.regression("36339")
@@ -2301,7 +2295,7 @@ def test_compiler_with_custom_non_numeric_version(self, mock_executable):
}
]
spack.config.set("compilers", compiler_configuration)
s = Spec("pkg-a %gcc@foo").concretized()
s = Spec("a %gcc@foo").concretized()
assert s.compiler.version == ver("=foo")
@pytest.mark.regression("36628")
@@ -2327,13 +2321,13 @@ def test_concretization_with_compilers_supporting_target_any(self):
]
with spack.config.override("compilers", compiler_configuration):
s = Spec("pkg-a").concretized()
s = spack.spec.Spec("a").concretized()
assert s.satisfies("%gcc@12.1.0")
@pytest.mark.parametrize("spec_str", ["mpileaks", "mpileaks ^mpich"])
def test_virtuals_are_annotated_on_edges(self, spec_str):
def test_virtuals_are_annotated_on_edges(self, spec_str, default_mock_concretization):
"""Tests that information on virtuals is annotated on DAG edges"""
spec = Spec(spec_str).concretized()
spec = default_mock_concretization(spec_str)
mpi_provider = spec["mpi"].name
edges = spec.edges_to_dependencies(name=mpi_provider)
@@ -2347,7 +2341,7 @@ def test_virtuals_are_annotated_on_edges(self, spec_str):
"spec_str,mpi_name",
[("mpileaks", "mpich"), ("mpileaks ^mpich2", "mpich2"), ("mpileaks ^zmpi", "zmpi")],
)
def test_virtuals_are_reconstructed_on_reuse(self, spec_str, mpi_name, mutable_database):
def test_virtuals_are_reconstructed_on_reuse(self, spec_str, mpi_name, database):
"""Tests that when we reuse a spec, virtual on edges are reconstructed correctly"""
with spack.config.override("concretizer:reuse", True):
spec = Spec(spec_str).concretized()
@@ -2362,7 +2356,7 @@ def test_dont_define_new_version_from_input_if_checksum_required(self, working_e
with pytest.raises(spack.error.UnsatisfiableSpecError):
# normally spack concretizes to @=3.0 if it's not defined in package.py, except
# when checksums are required
Spec("pkg-a@=3.0").concretized()
Spec("a@=3.0").concretized()
@pytest.mark.regression("39570")
@pytest.mark.db
@@ -2462,7 +2456,7 @@ def _default_libc(self):
spack.util.libc, "libc_from_current_python_process", lambda: Spec("glibc@=2.28")
)
mutable_config.set("config:install_missing_compilers", True)
s = Spec("pkg-a %gcc@=13.2.0").concretized()
s = Spec("a %gcc@=13.2.0").concretized()
assert s.satisfies("%gcc@13.2.0")
@pytest.mark.regression("43267")
@@ -2595,17 +2589,17 @@ def test_cannot_reuse_host_incompatible_libc(self):
# We install b@1 ^glibc@2.30, and b@0 ^glibc@2.28. The former is not host compatible, the
# latter is.
fst = Spec("pkg-b@1").concretized()
fst = Spec("b@1").concretized()
fst._mark_concrete(False)
fst.dependencies("glibc")[0].versions = VersionList(["=2.30"])
fst._mark_concrete(True)
snd = Spec("pkg-b@0").concretized()
snd = Spec("b@0").concretized()
# The spec b@1 ^glibc@2.30 is "more optimal" than b@0 ^glibc@2.28, but due to glibc
# incompatibility, it should not be reused.
solver = spack.solver.asp.Solver()
setup = spack.solver.asp.SpackSolverSetup()
result, _, _ = solver.driver.solve(setup, [Spec("pkg-b")], reuse=[fst, snd])
result, _, _ = solver.driver.solve(setup, [Spec("b")], reuse=[fst, snd])
assert len(result.specs) == 1
assert result.specs[0] == snd
@@ -2810,9 +2804,7 @@ def test_drop_moving_targets(v_str, v_opts, checksummed):
class TestConcreteSpecsByHash:
"""Tests the container of concrete specs"""
@pytest.mark.parametrize(
"input_specs", [["pkg-a"], ["pkg-a foobar=bar", "pkg-b"], ["pkg-a foobar=baz", "pkg-b"]]
)
@pytest.mark.parametrize("input_specs", [["a"], ["a foobar=bar", "b"], ["a foobar=baz", "b"]])
def test_adding_specs(self, input_specs, default_mock_concretization):
"""Tests that concrete specs in the container are equivalent, but stored as different
objects in memory.
@@ -3008,7 +3000,7 @@ def test_concretization_version_order():
),
],
)
@pytest.mark.usefixtures("mutable_database", "mock_store")
@pytest.mark.usefixtures("database", "mock_store")
@pytest.mark.not_on_windows("Expected length is different on Windows")
def test_filtering_reused_specs(
roots, reuse_yaml, expected, not_expected, expected_length, mutable_config, monkeypatch
@@ -3029,7 +3021,7 @@ def test_filtering_reused_specs(
assert all(not x.satisfies(constraint) for x in specs)
@pytest.mark.usefixtures("mutable_database", "mock_store")
@pytest.mark.usefixtures("database", "mock_store")
@pytest.mark.parametrize(
"reuse_yaml,expected_length",
[({"from": [{"type": "local"}]}, 17), ({"from": [{"type": "buildcache"}]}, 0)],
@@ -3065,7 +3057,9 @@ def test_spec_filters(specs, include, exclude, expected):
@pytest.mark.only_clingo("clingo only reuse feature being tested")
@pytest.mark.regression("38484")
def test_git_ref_version_can_be_reused(install_mockery, do_not_check_runtimes_on_reuse):
def test_git_ref_version_can_be_reused(
install_mockery_mutable_config, do_not_check_runtimes_on_reuse
):
first_spec = spack.spec.Spec("git-ref-package@git.2.1.5=2.1.5~opt").concretized()
first_spec.package.do_install(fake=True, explicit=True)
@@ -3084,7 +3078,7 @@ def test_git_ref_version_can_be_reused(install_mockery, do_not_check_runtimes_on
@pytest.mark.only_clingo("clingo only reuse feature being tested")
@pytest.mark.parametrize("standard_version", ["2.0.0", "2.1.5", "2.1.6"])
def test_reuse_prefers_standard_over_git_versions(
standard_version, install_mockery, do_not_check_runtimes_on_reuse
standard_version, install_mockery_mutable_config, do_not_check_runtimes_on_reuse
):
"""
order matters in this test. typically reuse would pick the highest versioned installed match

View File

@@ -9,7 +9,6 @@
import archspec.cpu
import spack.config
import spack.paths
import spack.repo
import spack.solver.asp
@@ -33,7 +32,7 @@ def _concretize_with_reuse(*, root_str, reused_str):
@pytest.fixture
def runtime_repo(mutable_config):
def runtime_repo(config):
repo = os.path.join(spack.paths.repos_path, "compiler_runtime.test")
with spack.repo.use_repositories(repo) as mock_repo:
yield mock_repo
@@ -48,8 +47,8 @@ def enable_runtimes():
def test_correct_gcc_runtime_is_injected_as_dependency(runtime_repo):
s = spack.spec.Spec("pkg-a%gcc@10.2.1 ^pkg-b%gcc@9.4.0").concretized()
a, b = s["pkg-a"], s["pkg-b"]
s = spack.spec.Spec("a%gcc@10.2.1 ^b%gcc@9.4.0").concretized()
a, b = s["a"], s["b"]
# Both a and b should depend on the same gcc-runtime directly
assert a.dependencies("gcc-runtime") == b.dependencies("gcc-runtime")
@@ -62,16 +61,16 @@ def test_correct_gcc_runtime_is_injected_as_dependency(runtime_repo):
def test_external_nodes_do_not_have_runtimes(runtime_repo, mutable_config, tmp_path):
"""Tests that external nodes don't have runtime dependencies."""
packages_yaml = {"pkg-b": {"externals": [{"spec": "pkg-b@1.0", "prefix": f"{str(tmp_path)}"}]}}
packages_yaml = {"b": {"externals": [{"spec": "b@1.0", "prefix": f"{str(tmp_path)}"}]}}
spack.config.set("packages", packages_yaml)
s = spack.spec.Spec("pkg-a%gcc@10.2.1").concretized()
s = spack.spec.Spec("a%gcc@10.2.1").concretized()
a, b = s["pkg-a"], s["pkg-b"]
a, b = s["a"], s["b"]
# Since b is an external, it doesn't depend on gcc-runtime
assert a.dependencies("gcc-runtime")
assert a.dependencies("pkg-b")
assert a.dependencies("b")
assert not b.dependencies("gcc-runtime")
@@ -79,36 +78,23 @@ def test_external_nodes_do_not_have_runtimes(runtime_repo, mutable_config, tmp_p
"root_str,reused_str,expected,nruntime",
[
# The reused runtime is older than we need, thus we'll add a more recent one for a
(
"pkg-a%gcc@10.2.1",
"pkg-b%gcc@9.4.0",
{"pkg-a": "gcc-runtime@10.2.1", "pkg-b": "gcc-runtime@9.4.0"},
2,
),
("a%gcc@10.2.1", "b%gcc@9.4.0", {"a": "gcc-runtime@10.2.1", "b": "gcc-runtime@9.4.0"}, 2),
# The root is compiled with an older compiler, thus we'll NOT reuse the runtime from b
(
"pkg-a%gcc@9.4.0",
"pkg-b%gcc@10.2.1",
{"pkg-a": "gcc-runtime@9.4.0", "pkg-b": "gcc-runtime@9.4.0"},
1,
),
("a%gcc@9.4.0", "b%gcc@10.2.1", {"a": "gcc-runtime@9.4.0", "b": "gcc-runtime@9.4.0"}, 1),
# Same as before, but tests that we can reuse from a more generic target
pytest.param(
"pkg-a%gcc@9.4.0",
"pkg-b%gcc@10.2.1 target=x86_64",
{"pkg-a": "gcc-runtime@9.4.0", "pkg-b": "gcc-runtime@9.4.0"},
"a%gcc@9.4.0",
"b%gcc@10.2.1 target=x86_64",
{"a": "gcc-runtime@9.4.0", "b": "gcc-runtime@9.4.0"},
1,
marks=pytest.mark.skipif(
str(archspec.cpu.host().family) != "x86_64", reason="test data is x86_64 specific"
),
),
pytest.param(
"pkg-a%gcc@10.2.1",
"pkg-b%gcc@9.4.0 target=x86_64",
{
"pkg-a": "gcc-runtime@10.2.1 target=x86_64",
"pkg-b": "gcc-runtime@9.4.0 target=x86_64",
},
"a%gcc@10.2.1",
"b%gcc@9.4.0 target=x86_64",
{"a": "gcc-runtime@10.2.1 target=x86_64", "b": "gcc-runtime@9.4.0 target=x86_64"},
2,
marks=pytest.mark.skipif(
str(archspec.cpu.host().family) != "x86_64", reason="test data is x86_64 specific"
@@ -126,9 +112,9 @@ def test_reusing_specs_with_gcc_runtime(root_str, reused_str, expected, nruntime
root, reused_spec = _concretize_with_reuse(root_str=root_str, reused_str=reused_str)
runtime_a = root.dependencies("gcc-runtime")[0]
assert runtime_a.satisfies(expected["pkg-a"])
runtime_b = root["pkg-b"].dependencies("gcc-runtime")[0]
assert runtime_b.satisfies(expected["pkg-b"])
assert runtime_a.satisfies(expected["a"])
runtime_b = root["b"].dependencies("gcc-runtime")[0]
assert runtime_b.satisfies(expected["b"])
runtimes = [x for x in root.traverse() if x.name == "gcc-runtime"]
assert len(runtimes) == nruntime
@@ -139,7 +125,7 @@ def test_reusing_specs_with_gcc_runtime(root_str, reused_str, expected, nruntime
[
# Ensure that, whether we have multiple runtimes in the DAG or not,
# we always link only the latest version
("pkg-a%gcc@10.2.1", "pkg-b%gcc@9.4.0", ["gcc-runtime@10.2.1"], ["gcc-runtime@9.4.0"])
("a%gcc@10.2.1", "b%gcc@9.4.0", ["gcc-runtime@10.2.1"], ["gcc-runtime@9.4.0"])
],
)
def test_views_can_handle_duplicate_runtime_nodes(

View File

@@ -161,24 +161,21 @@ def test_preferred_providers(self):
spec = concretize("mpileaks")
assert "zmpi" in spec
@pytest.mark.parametrize(
"update,expected",
[
(
{"url": "http://www.somewhereelse.com/mpileaks-1.0.tar.gz"},
"http://www.somewhereelse.com/mpileaks-2.3.tar.gz",
),
({}, "http://www.llnl.gov/mpileaks-2.3.tar.gz"),
],
)
def test_config_set_pkg_property_url(self, update, expected, mock_repo_path):
def test_config_set_pkg_property_url(self, mutable_mock_repo):
"""Test setting an existing attribute in the package class"""
update_packages("mpileaks", "package_attributes", update)
with spack.repo.use_repositories(mock_repo_path):
spec = concretize("mpileaks")
assert spec.package.fetcher.url == expected
update_packages(
"mpileaks",
"package_attributes",
{"url": "http://www.somewhereelse.com/mpileaks-1.0.tar.gz"},
)
spec = concretize("mpileaks")
assert spec.package.fetcher.url == "http://www.somewhereelse.com/mpileaks-2.3.tar.gz"
def test_config_set_pkg_property_new(self, mock_repo_path):
update_packages("mpileaks", "package_attributes", {})
spec = concretize("mpileaks")
assert spec.package.fetcher.url == "http://www.llnl.gov/mpileaks-2.3.tar.gz"
def test_config_set_pkg_property_new(self, mutable_mock_repo):
"""Test that you can set arbitrary attributes on the Package class"""
conf = syaml.load_config(
"""\
@@ -197,20 +194,19 @@ def test_config_set_pkg_property_new(self, mock_repo_path):
"""
)
spack.config.set("packages", conf, scope="concretize")
with spack.repo.use_repositories(mock_repo_path):
spec = concretize("mpileaks")
assert spec.package.v1 == 1
assert spec.package.v2 is True
assert spec.package.v3 == "yesterday"
assert spec.package.v4 == "true"
assert dict(spec.package.v5) == {"x": 1, "y": 2}
assert list(spec.package.v6) == [1, 2]
spec = concretize("mpileaks")
assert spec.package.v1 == 1
assert spec.package.v2 is True
assert spec.package.v3 == "yesterday"
assert spec.package.v4 == "true"
assert dict(spec.package.v5) == {"x": 1, "y": 2}
assert list(spec.package.v6) == [1, 2]
update_packages("mpileaks", "package_attributes", {})
with spack.repo.use_repositories(mock_repo_path):
spec = concretize("mpileaks")
with pytest.raises(AttributeError):
spec.package.v1
spec = concretize("mpileaks")
with pytest.raises(AttributeError):
spec.package.v1
def test_preferred(self):
""" "Test packages with some version marked as preferred=True"""
@@ -516,5 +512,5 @@ def test_default_preference_variant_different_type_does_not_error(self):
packages.yaml doesn't fail with an error.
"""
with spack.config.override("packages:all", {"variants": "+foo"}):
s = Spec("pkg-a").concretized()
s = Spec("a").concretized()
assert s.satisfies("foo=bar")

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