Compare commits

..

4 Commits

Author SHA1 Message Date
Wouter Deconinck
a5f8cfd3f3 RPackages with custom url: url -> urls=[url] 2024-08-20 13:27:57 -05:00
Wouter Deconinck
d38b4024bc RPackages with bioc: remove redundant homepage and url 2024-08-20 13:27:24 -05:00
Wouter Deconinck
4d09bc13d9 RPackage: urls=[bioc/src/contrib,data/annotation/src/contrib] for bioc 2024-08-20 13:22:50 -05:00
Wouter Deconinck
1794cd598c RPackage: urls=[src/contrib,src/contrib/Archive], list_url=src/contrib 2024-08-20 13:21:41 -05:00
1241 changed files with 13597 additions and 20085 deletions

View File

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

View File

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

View File

@@ -28,8 +28,8 @@ jobs:
run: run:
shell: ${{ matrix.system.shell }} shell: ${{ matrix.system.shell }}
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
with: with:
python-version: ${{inputs.python_version}} python-version: ${{inputs.python_version}}
- name: Install Python packages - name: Install Python packages
@@ -40,8 +40,6 @@ jobs:
run: | run: |
python -m pip install --upgrade pywin32 python -m pip install --upgrade pywin32
- name: Package audits (with coverage) - name: Package audits (with coverage)
env:
COVERAGE_FILE: coverage/.coverage-audits-${{ matrix.system.os }}
if: ${{ inputs.with_coverage == 'true' && runner.os != 'Windows' }} if: ${{ inputs.with_coverage == 'true' && runner.os != 'Windows' }}
run: | run: |
. share/spack/setup-env.sh . share/spack/setup-env.sh
@@ -49,26 +47,27 @@ jobs:
coverage run $(which spack) audit configs coverage run $(which spack) audit configs
coverage run $(which spack) -d audit externals coverage run $(which spack) -d audit externals
coverage combine coverage combine
coverage xml
- name: Package audits (without coverage) - name: Package audits (without coverage)
if: ${{ inputs.with_coverage == 'false' && runner.os != 'Windows' }} if: ${{ inputs.with_coverage == 'false' && runner.os != 'Windows' }}
run: | run: |
. share/spack/setup-env.sh . share/spack/setup-env.sh
spack -d audit packages spack -d audit packages
spack -d audit configs spack -d audit configs
spack -d audit externals spack -d audit externals
- name: Package audits (without coverage) - name: Package audits (without coverage)
if: ${{ runner.os == 'Windows' }} if: ${{ runner.os == 'Windows' }}
run: | run: |
. share/spack/setup-env.sh . share/spack/setup-env.sh
spack -d audit packages spack -d audit packages
./share/spack/qa/validate_last_exit.ps1 ./share/spack/qa/validate_last_exit.ps1
spack -d audit configs spack -d audit configs
./share/spack/qa/validate_last_exit.ps1 ./share/spack/qa/validate_last_exit.ps1
spack -d audit externals spack -d audit externals
./share/spack/qa/validate_last_exit.ps1 ./share/spack/qa/validate_last_exit.ps1
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673
if: ${{ inputs.with_coverage == 'true' && runner.os != 'Windows' }} if: ${{ inputs.with_coverage == 'true' }}
with: with:
name: coverage-audits-${{ matrix.system.os }} flags: unittests,audits
path: coverage token: ${{ secrets.CODECOV_TOKEN }}
include-hidden-files: true verbose: true

View File

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

View File

@@ -55,7 +55,7 @@ jobs:
if: github.repository == 'spack/spack' if: github.repository == 'spack/spack'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 - uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81
id: docker_meta id: docker_meta
@@ -87,7 +87,7 @@ jobs:
fi fi
- name: Upload Dockerfile - name: Upload Dockerfile
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a
with: with:
name: dockerfiles_${{ matrix.dockerfile[0] }} name: dockerfiles_${{ matrix.dockerfile[0] }}
path: dockerfiles path: dockerfiles
@@ -126,7 +126,7 @@ jobs:
needs: deploy-images needs: deploy-images
steps: steps:
- name: Merge Artifacts - name: Merge Artifacts
uses: actions/upload-artifact/merge@50769540e7f4bd5e21e526ee35c689e35e0d6874 uses: actions/upload-artifact/merge@834a144ee995460fba8ed112a2fc961b36a5ec5a
with: with:
name: dockerfiles name: dockerfiles
pattern: dockerfiles_* pattern: dockerfiles_*

View File

@@ -36,7 +36,7 @@ jobs:
core: ${{ steps.filter.outputs.core }} core: ${{ steps.filter.outputs.core }}
packages: ${{ steps.filter.outputs.packages }} packages: ${{ steps.filter.outputs.packages }}
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
if: ${{ github.event_name == 'push' }} if: ${{ github.event_name == 'push' }}
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -84,30 +84,8 @@ jobs:
needs: [ prechecks, changes ] needs: [ prechecks, changes ]
uses: ./.github/workflows/unit_tests.yaml uses: ./.github/workflows/unit_tests.yaml
secrets: inherit secrets: inherit
upload-coverage:
needs: [ unit-tests, prechecks ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
with:
fetch-depth: 0
- name: Download coverage files
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
pattern: coverage-*
path: coverage
merge-multiple: true
- run: pip install --upgrade coverage
- run: ls -la coverage
- run: coverage combine -a coverage/.coverage*
- run: coverage xml
- name: "Upload coverage"
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673
with:
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
all: all:
needs: [ upload-coverage, bootstrap ] needs: [ unit-tests, bootstrap ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Success - name: Success

View File

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

View File

@@ -40,10 +40,10 @@ jobs:
on_develop: false on_develop: false
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install System packages - name: Install System packages
@@ -76,23 +76,22 @@ jobs:
SPACK_PYTHON: python SPACK_PYTHON: python
SPACK_TEST_PARALLEL: 2 SPACK_TEST_PARALLEL: 2
COVERAGE: true COVERAGE: true
COVERAGE_FILE: coverage/.coverage-${{ matrix.os }}-python${{ matrix.python-version }}
UNIT_TEST_COVERAGE: ${{ matrix.python-version == '3.11' }} UNIT_TEST_COVERAGE: ${{ matrix.python-version == '3.11' }}
run: | run: |
share/spack/qa/run-unit-tests share/spack/qa/run-unit-tests
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673
with: with:
name: coverage-${{ matrix.os }}-python${{ matrix.python-version }} flags: unittests,linux,${{ matrix.concretizer }}
path: coverage token: ${{ secrets.CODECOV_TOKEN }}
include-hidden-files: true verbose: true
# Test shell integration # Test shell integration
shell: shell:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
with: with:
python-version: '3.11' python-version: '3.11'
- name: Install System packages - name: Install System packages
@@ -113,11 +112,11 @@ jobs:
COVERAGE: true COVERAGE: true
run: | run: |
share/spack/qa/run-shell-tests share/spack/qa/run-shell-tests
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673
with: with:
name: coverage-shell flags: shelltests,linux
path: coverage token: ${{ secrets.CODECOV_TOKEN }}
include-hidden-files: true verbose: true
# Test RHEL8 UBI with platform Python. This job is run # Test RHEL8 UBI with platform Python. This job is run
# only on PRs modifying core Spack # only on PRs modifying core Spack
@@ -130,7 +129,7 @@ jobs:
dnf install -y \ dnf install -y \
bzip2 curl file gcc-c++ gcc gcc-gfortran git gnupg2 gzip \ bzip2 curl file gcc-c++ gcc gcc-gfortran git gnupg2 gzip \
make patch tcl unzip which xz make patch tcl unzip which xz
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- name: Setup repo and non-root user - name: Setup repo and non-root user
run: | run: |
git --version git --version
@@ -149,10 +148,10 @@ jobs:
clingo-cffi: clingo-cffi:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
with: with:
python-version: '3.11' python-version: '3.11'
- name: Install System packages - name: Install System packages
@@ -171,14 +170,13 @@ jobs:
- name: Run unit tests (full suite with coverage) - name: Run unit tests (full suite with coverage)
env: env:
COVERAGE: true COVERAGE: true
COVERAGE_FILE: coverage/.coverage-clingo-cffi
run: | run: |
share/spack/qa/run-unit-tests share/spack/qa/run-unit-tests
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673
with: with:
name: coverage-clingo-cffi flags: unittests,linux,clingo
path: coverage token: ${{ secrets.CODECOV_TOKEN }}
include-hidden-files: true verbose: true
# Run unit tests on MacOS # Run unit tests on MacOS
macos: macos:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -187,10 +185,10 @@ jobs:
os: [macos-13, macos-14] os: [macos-13, macos-14]
python-version: ["3.11"] python-version: ["3.11"]
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install Python packages - name: Install Python packages
@@ -203,7 +201,6 @@ jobs:
- name: Run unit tests - name: Run unit tests
env: env:
SPACK_TEST_PARALLEL: 4 SPACK_TEST_PARALLEL: 4
COVERAGE_FILE: coverage/.coverage-${{ matrix.os }}-python${{ matrix.python-version }}
run: | run: |
git --version git --version
. .github/workflows/bin/setup_git.sh . .github/workflows/bin/setup_git.sh
@@ -212,11 +209,11 @@ jobs:
$(which spack) solve zlib $(which spack) solve zlib
common_args=(--dist loadfile --tx '4*popen//python=./bin/spack-tmpconfig python -u ./bin/spack python' -x) common_args=(--dist loadfile --tx '4*popen//python=./bin/spack-tmpconfig python -u ./bin/spack python' -x)
$(which spack) unit-test --verbose --cov --cov-config=pyproject.toml --cov-report=xml:coverage.xml "${common_args[@]}" $(which spack) unit-test --verbose --cov --cov-config=pyproject.toml --cov-report=xml:coverage.xml "${common_args[@]}"
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673
with: with:
name: coverage-${{ matrix.os }}-python${{ matrix.python-version }} flags: unittests,macos
path: coverage token: ${{ secrets.CODECOV_TOKEN }}
include-hidden-files: true verbose: true
# Run unit tests on Windows # Run unit tests on Windows
windows: windows:
defaults: defaults:
@@ -225,10 +222,10 @@ jobs:
powershell Invoke-Expression -Command "./share/spack/qa/windows_test_setup.ps1"; {0} powershell Invoke-Expression -Command "./share/spack/qa/windows_test_setup.ps1"; {0}
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
with: with:
python-version: 3.9 python-version: 3.9
- name: Install Python packages - name: Install Python packages
@@ -238,13 +235,13 @@ jobs:
run: | run: |
./.github/workflows/bin/setup_git.ps1 ./.github/workflows/bin/setup_git.ps1
- name: Unit Test - name: Unit Test
env:
COVERAGE_FILE: coverage/.coverage-windows
run: | run: |
spack unit-test -x --verbose --cov --cov-config=pyproject.toml spack unit-test -x --verbose --cov --cov-config=pyproject.toml
./share/spack/qa/validate_last_exit.ps1 ./share/spack/qa/validate_last_exit.ps1
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 coverage combine -a
coverage xml
- uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673
with: with:
name: coverage-windows flags: unittests,windows
path: coverage token: ${{ secrets.CODECOV_TOKEN }}
include-hidden-files: true verbose: true

View File

@@ -18,8 +18,8 @@ jobs:
validate: validate:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
with: with:
python-version: '3.11' python-version: '3.11'
cache: 'pip' cache: 'pip'
@@ -35,10 +35,10 @@ jobs:
style: style:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f
with: with:
python-version: '3.11' python-version: '3.11'
cache: 'pip' cache: 'pip'
@@ -70,7 +70,7 @@ jobs:
dnf install -y \ dnf install -y \
bzip2 curl file gcc-c++ gcc gcc-gfortran git gnupg2 gzip \ bzip2 curl file gcc-c++ gcc gcc-gfortran git gnupg2 gzip \
make patch tcl unzip which xz make patch tcl unzip which xz
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- name: Setup repo and non-root user - name: Setup repo and non-root user
run: | run: |
git --version git --version
@@ -87,62 +87,3 @@ jobs:
spack -d bootstrap now --dev spack -d bootstrap now --dev
spack style -t black spack style -t black
spack unit-test -V spack unit-test -V
import-check:
runs-on: ubuntu-latest
steps:
- uses: julia-actions/setup-julia@v2
with:
version: '1.10'
- uses: julia-actions/cache@v2
# PR: use the base of the PR as the old commit
- name: Checkout PR base commit
if: github.event_name == 'pull_request'
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
with:
ref: ${{ github.event.pull_request.base.sha }}
path: old
# not a PR: use the previous commit as the old commit
- name: Checkout previous commit
if: github.event_name != 'pull_request'
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
with:
fetch-depth: 2
path: old
- name: Checkout previous commit
if: github.event_name != 'pull_request'
run: git -C old reset --hard HEAD^
- name: Checkout new commit
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
with:
path: new
- name: Install circular import checker
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
with:
repository: haampie/circular-import-fighter
ref: 555519c6fd5564fd2eb844e7b87e84f4d12602e2
path: circular-import-fighter
- name: Install dependencies
working-directory: circular-import-fighter
run: make -j dependencies
- name: Import cycles before
working-directory: circular-import-fighter
run: make SPACK_ROOT=../old && cp solution solution.old
- name: Import cycles after
working-directory: circular-import-fighter
run: make clean-graph && make SPACK_ROOT=../new && cp solution solution.new
- name: Compare import cycles
working-directory: circular-import-fighter
run: |
edges_before="$(grep -oP 'edges to delete: \K\d+' solution.old)"
edges_after="$(grep -oP 'edges to delete: \K\d+' solution.new)"
if [ "$edges_after" -gt "$edges_before" ]; then
printf '\033[1;31mImport check failed: %s imports need to be deleted, ' "$edges_after"
printf 'previously this was %s\033[0m\n' "$edges_before"
printf 'Compare \033[1;97m"Import cycles before"\033[0m and '
printf '\033[1;97m"Import cycles after"\033[0m to see problematic imports.\n'
exit 1
else
printf '\033[1;32mImport check passed: %s <= %s\033[0m\n' "$edges_after" "$edges_before"
fi

View File

@@ -46,18 +46,13 @@ See the
[Feature Overview](https://spack.readthedocs.io/en/latest/features.html) [Feature Overview](https://spack.readthedocs.io/en/latest/features.html)
for examples and highlights. for examples and highlights.
To install spack and your first package, make sure you have Python & Git. To install spack and your first package, make sure you have Python.
Then: Then:
$ git clone -c feature.manyFiles=true --depth=2 https://github.com/spack/spack.git $ git clone -c feature.manyFiles=true https://github.com/spack/spack.git
$ cd spack/bin $ cd spack/bin
$ ./spack install zlib $ ./spack install zlib
> [!TIP]
> `-c feature.manyFiles=true` improves git's performance on repositories with 1,000+ files.
>
> `--depth=2` prunes the git history to reduce the size of the Spack installation.
Documentation Documentation
---------------- ----------------

View File

@@ -115,6 +115,12 @@ config:
suppress_gpg_warnings: false suppress_gpg_warnings: false
# If set to true, Spack will attempt to build any compiler on the spec
# that is not already available. If set to False, Spack will only use
# compilers already configured in compilers.yaml
install_missing_compilers: false
# If set to true, Spack will always check checksums after downloading # If set to true, Spack will always check checksums after downloading
# archives. If false, Spack skips the checksum step. # archives. If false, Spack skips the checksum step.
checksum: true checksum: true

View File

@@ -72,13 +72,3 @@ packages:
permissions: permissions:
read: world read: world
write: user write: user
cray-mpich:
buildable: false
cray-mvapich2:
buildable: false
fujitsu-mpi:
buildable: false
hpcx-mpi:
buildable: false
spectrum-mpi:
buildable: false

View File

@@ -1175,17 +1175,6 @@ unspecified version, but packages can depend on other packages with
could depend on ``mpich@1.2:`` if it can only build with version could depend on ``mpich@1.2:`` if it can only build with version
``1.2`` or higher of ``mpich``. ``1.2`` or higher of ``mpich``.
.. note:: Windows Spec Syntax Caveats
Windows has a few idiosyncrasies when it comes to the Spack spec syntax and the use of certain shells
Spack's spec dependency syntax uses the carat (``^``) character, however this is an escape string in CMD
so it must be escaped with an additional carat (i.e. ``^^``).
CMD also will attempt to interpret strings with ``=`` characters in them. Any spec including this symbol
must double quote the string.
Note: All of these issues are unique to CMD, they can be avoided by using Powershell.
For more context on these caveats see the related issues: `carat <https://github.com/spack/spack/issues/42833>`_ and `equals <https://github.com/spack/spack/issues/43348>`_
Below are more details about the specifiers that you can add to specs. Below are more details about the specifiers that you can add to specs.
.. _version-specifier: .. _version-specifier:

View File

@@ -5,9 +5,9 @@
.. chain: .. chain:
============================================= ============================
Chaining Spack Installations (upstreams.yaml) Chaining Spack Installations
============================================= ============================
You can point your Spack installation to another installation to use any You can point your Spack installation to another installation to use any
packages that are installed there. To register the other Spack instance, packages that are installed there. To register the other Spack instance,

View File

@@ -218,8 +218,6 @@ def setup(sphinx):
("py:class", "spack.spec.SpecfileReaderBase"), ("py:class", "spack.spec.SpecfileReaderBase"),
("py:class", "spack.install_test.Pb"), ("py:class", "spack.install_test.Pb"),
("py:class", "spack.filesystem_view.SimpleFilesystemView"), ("py:class", "spack.filesystem_view.SimpleFilesystemView"),
("py:class", "spack.traverse.EdgeAndDepth"),
("py:class", "archspec.cpu.microarchitecture.Microarchitecture"),
] ]
# The reST default role (used for this markup: `text`) to use for all documents. # The reST default role (used for this markup: `text`) to use for all documents.

View File

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

View File

@@ -414,13 +414,7 @@ default, it will also clone the package to a subdirectory in the
environment. This package will have a special variant ``dev_path`` environment. This package will have a special variant ``dev_path``
set, and Spack will ensure the package and its dependents are rebuilt set, and Spack will ensure the package and its dependents are rebuilt
any time the environment is installed if the package's local source any time the environment is installed if the package's local source
code has been modified. Spack's native implementation to check for modifications code has been modified. Spack ensures that all instances of a
is to check if ``mtime`` is newer than the installation.
A custom check can be created by overriding the ``detect_dev_src_change`` method
in your package class. This is particularly useful for projects using custom spack repo's
to drive development and want to optimize performance.
Spack ensures that all instances of a
developed package in the environment are concretized to match the developed package in the environment are concretized to match the
version (and other constraints) passed as the spec argument to the version (and other constraints) passed as the spec argument to the
``spack develop`` command. ``spack develop`` command.
@@ -869,7 +863,7 @@ named list ``compilers`` is ``['%gcc', '%clang', '%intel']`` on
spack: spack:
definitions: definitions:
- compilers: ['%gcc', '%clang'] - compilers: ['%gcc', '%clang']
- when: arch.satisfies('target=x86_64:') - when: arch.satisfies('x86_64:')
compilers: ['%intel'] compilers: ['%intel']
.. note:: .. note::

View File

@@ -61,15 +61,10 @@ Getting Spack is easy. You can clone it from the `github repository
.. code-block:: console .. code-block:: console
$ git clone -c feature.manyFiles=true --depth=2 https://github.com/spack/spack.git $ git clone -c feature.manyFiles=true https://github.com/spack/spack.git
This will create a directory called ``spack``. This will create a directory called ``spack``.
.. note::
``-c feature.manyFiles=true`` improves git's performance on repositories with 1,000+ files.
``--depth=2`` prunes the git history to reduce the size of the Spack installation.
.. _shell-support: .. _shell-support:
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
@@ -1480,14 +1475,16 @@ in a Windows CMD prompt.
Step 3: Run and configure Spack Step 3: Run and configure Spack
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
On Windows, Spack supports both primary native shells, Powershell and the traditional command prompt. To use Spack, run ``bin\spack_cmd.bat`` (you may need to Run as Administrator) from the top-level spack
To use Spack, pick your favorite shell, and run ``bin\spack_cmd.bat`` or ``share/spack/setup-env.ps1`` directory. This will provide a Windows command prompt with an environment properly set up with Spack
(you may need to Run as Administrator) from the top-level spack and its prerequisites. If you receive a warning message that Python is not in your ``PATH``
directory. This will provide a Spack enabled shell. If you receive a warning message that Python is not in your ``PATH``
(which may happen if you installed Python from the website and not the Windows Store) add the location (which may happen if you installed Python from the website and not the Windows Store) add the location
of the Python executable to your ``PATH`` now. You can permanently add Python to your ``PATH`` variable of the Python executable to your ``PATH`` now. You can permanently add Python to your ``PATH`` variable
by using the ``Edit the system environment variables`` utility in Windows Control Panel. by using the ``Edit the system environment variables`` utility in Windows Control Panel.
.. note::
Alternatively, Powershell can be used in place of CMD
To configure Spack, first run the following command inside the Spack console: To configure Spack, first run the following command inside the Spack console:
.. code-block:: console .. code-block:: console
@@ -1552,7 +1549,7 @@ and not tabs, so ensure that this is the case when editing one directly.
.. note:: Cygwin .. note:: Cygwin
The use of Cygwin is not officially supported by Spack and is not tested. The use of Cygwin is not officially supported by Spack and is not tested.
However Spack will not prevent this, so use if choosing to use Spack However Spack will not throw an error, so use if choosing to use Spack
with Cygwin, know that no functionality is garunteed. with Cygwin, know that no functionality is garunteed.
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
@@ -1566,12 +1563,21 @@ Spack console via:
spack install cpuinfo spack install cpuinfo
If in the previous step, you did not have CMake or Ninja installed, running the command above should install both packages If in the previous step, you did not have CMake or Ninja installed, running the command above should bootstrap both packages
.. note:: Spec Syntax Caveats """""""""""""""""""""""""""
Windows has a few idiosyncrasies when it comes to the Spack spec syntax and the use of certain shells Windows Compatible Packages
See the Spack spec syntax doc for more information """""""""""""""""""""""""""
Not all spack packages currently have Windows support. Some are inherently incompatible with the
platform, and others simply have yet to be ported. To view the current set of packages with Windows
support, the list command should be used via `spack list -t windows`. If there's a package you'd like
to install on Windows but is not in that list, feel free to reach out to request the port or contribute
the port yourself.
.. note::
This is by no means a comprehensive list, some packages may have ports that were not tagged
while others may just work out of the box on Windows and have not been tagged as such.
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
For developers For developers
@@ -1581,3 +1587,6 @@ The intent is to provide a Windows installer that will automatically set up
Python, Git, and Spack, instead of requiring the user to do so manually. Python, Git, and Spack, instead of requiring the user to do so manually.
Instructions for creating the installer are at Instructions for creating the installer are at
https://github.com/spack/spack/blob/develop/lib/spack/spack/cmd/installer/README.md https://github.com/spack/spack/blob/develop/lib/spack/spack/cmd/installer/README.md
Alternatively a pre-built copy of the Windows installer is available as an artifact of Spack's Windows CI
available at each run of the CI on develop or any PR.

View File

@@ -39,15 +39,10 @@ package:
.. code-block:: console .. code-block:: console
$ git clone -c feature.manyFiles=true --depth=2 https://github.com/spack/spack.git $ git clone -c feature.manyFiles=true https://github.com/spack/spack.git
$ cd spack/bin $ cd spack/bin
$ ./spack install libelf $ ./spack install libelf
.. note::
``-c feature.manyFiles=true`` improves git's performance on repositories with 1,000+ files.
``--depth=2`` prunes the git history to reduce the size of the Spack installation.
If you're new to spack and want to start using it, see :doc:`getting_started`, If you're new to spack and want to start using it, see :doc:`getting_started`,
or refer to the full manual below. or refer to the full manual below.

View File

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

View File

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

View File

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

331
lib/spack/env/cc vendored
View File

@@ -101,9 +101,10 @@ setsep() {
esac esac
} }
# prepend LISTNAME ELEMENT # prepend LISTNAME ELEMENT [SEP]
# #
# Prepend ELEMENT to the list stored in the variable LISTNAME. # Prepend ELEMENT to the list stored in the variable LISTNAME,
# assuming the list is separated by SEP.
# Handles empty lists and single-element lists. # Handles empty lists and single-element lists.
prepend() { prepend() {
varname="$1" varname="$1"
@@ -118,39 +119,18 @@ prepend() {
fi fi
} }
# contains LISTNAME ELEMENT # append LISTNAME ELEMENT [SEP]
# #
# Test whether LISTNAME contains ELEMENT. # Append ELEMENT to the list stored in the variable LISTNAME,
# Set $? to 1 if LISTNAME does not contain ELEMENT. # assuming the list is separated by SEP.
# Set $? to 0 if LISTNAME does not contain ELEMENT.
contains() {
varname="$1"
elt="$2"
setsep "$varname"
# the list may: 1) only contain the element, 2) start with the element,
# 3) contain the element in the middle, or 4) end wtih the element.
eval "[ \"\${$varname}\" = \"$elt\" ]" \
|| eval "[ \"\${$varname#${elt}${sep}}\" != \"\${$varname}\" ]" \
|| eval "[ \"\${$varname#*${sep}${elt}${sep}}\" != \"\${$varname}\" ]" \
|| eval "[ \"\${$varname%${sep}${elt}}\" != \"\${$varname}\" ]"
}
# append LISTNAME ELEMENT [unique]
#
# Append ELEMENT to the list stored in the variable LISTNAME.
# Handles empty lists and single-element lists. # Handles empty lists and single-element lists.
#
# If the third argument is provided and if it is the string 'unique',
# this will not append if ELEMENT is already in the list LISTNAME.
append() { append() {
varname="$1" varname="$1"
elt="$2" elt="$2"
if empty "$varname"; then if empty "$varname"; then
eval "$varname=\"\${elt}\"" eval "$varname=\"\${elt}\""
elif [ "$3" != "unique" ] || ! contains "$varname" "$elt" ; then else
# Get the appropriate separator for the list we're appending to. # Get the appropriate separator for the list we're appending to.
setsep "$varname" setsep "$varname"
eval "$varname=\"\${$varname}${sep}\${elt}\"" eval "$varname=\"\${$varname}${sep}\${elt}\""
@@ -168,21 +148,10 @@ extend() {
if [ "$sep" != " " ]; then if [ "$sep" != " " ]; then
IFS="$sep" IFS="$sep"
fi fi
eval "for elt in \${$2}; do append $1 \"$3\${elt}\" ${_append_args}; done" eval "for elt in \${$2}; do append $1 \"$3\${elt}\"; done"
unset IFS unset IFS
} }
# extend_unique LISTNAME1 LISTNAME2 [PREFIX]
#
# Append the elements stored in the variable LISTNAME2 to the list
# stored in LISTNAME1, if they are not already present.
# If PREFIX is provided, prepend it to each element.
extend_unique() {
_append_args="unique"
extend "$@"
unset _append_args
}
# preextend LISTNAME1 LISTNAME2 [PREFIX] # preextend LISTNAME1 LISTNAME2 [PREFIX]
# #
# Prepend the elements stored in the list at LISTNAME2 # Prepend the elements stored in the list at LISTNAME2
@@ -269,36 +238,6 @@ esac
} }
" "
# path_list functions. Path_lists have 3 parts: spack_store_<list>, <list> and system_<list>,
# which are used to prioritize paths when assembling the final command line.
# init_path_lists LISTNAME
# Set <LISTNAME>, spack_store_<LISTNAME>, and system_<LISTNAME> to "".
init_path_lists() {
eval "spack_store_$1=\"\""
eval "$1=\"\""
eval "system_$1=\"\""
}
# assign_path_lists LISTNAME1 LISTNAME2
# Copy contents of LISTNAME2 into LISTNAME1, for each path_list prefix.
assign_path_lists() {
eval "spack_store_$1=\"\${spack_store_$2}\""
eval "$1=\"\${$2}\""
eval "system_$1=\"\${system_$2}\""
}
# append_path_lists LISTNAME ELT
# Append the provided ELT to the appropriate list, based on the result of path_order().
append_path_lists() {
path_order "$2"
case $? in
0) eval "append spack_store_$1 \"\$2\"" ;;
1) eval "append $1 \"\$2\"" ;;
2) eval "append system_$1 \"\$2\"" ;;
esac
}
# Check if optional parameters are defined # Check if optional parameters are defined
# If we aren't asking for debug flags, don't add them # If we aren't asking for debug flags, don't add them
if [ -z "${SPACK_ADD_DEBUG_FLAGS:-}" ]; then if [ -z "${SPACK_ADD_DEBUG_FLAGS:-}" ]; then
@@ -482,6 +421,26 @@ if [ "$mode" = vcheck ]; then
execute execute
fi fi
# Darwin's linker has a -r argument that merges object files together.
# It doesn't work with -rpath.
# This variable controls whether they are added.
add_rpaths=true
if [ "$mode" = ld ] || [ "$mode" = ccld ]; then
if [ "${SPACK_SHORT_SPEC#*darwin}" != "${SPACK_SHORT_SPEC}" ]; then
for arg in "$@"; do
if [ "$arg" = "-r" ]; then
if [ "$mode" = ld ] || [ "$mode" = ccld ]; then
add_rpaths=false
break
fi
elif [ "$arg" = "-Wl,-r" ] && [ "$mode" = ccld ]; then
add_rpaths=false
break
fi
done
fi
fi
# Save original command for debug logging # Save original command for debug logging
input_command="$*" input_command="$*"
@@ -511,7 +470,12 @@ input_command="$*"
parse_Wl() { parse_Wl() {
while [ $# -ne 0 ]; do while [ $# -ne 0 ]; do
if [ "$wl_expect_rpath" = yes ]; then if [ "$wl_expect_rpath" = yes ]; then
append_path_lists return_rpath_dirs_list "$1" path_order "$1"
case $? in
0) append return_spack_store_rpath_dirs_list "$1" ;;
1) append return_rpath_dirs_list "$1" ;;
2) append return_system_rpath_dirs_list "$1" ;;
esac
wl_expect_rpath=no wl_expect_rpath=no
else else
case "$1" in case "$1" in
@@ -520,14 +484,24 @@ parse_Wl() {
if [ -z "$arg" ]; then if [ -z "$arg" ]; then
shift; continue shift; continue
fi fi
append_path_lists return_rpath_dirs_list "$arg" path_order "$arg"
case $? in
0) append return_spack_store_rpath_dirs_list "$arg" ;;
1) append return_rpath_dirs_list "$arg" ;;
2) append return_system_rpath_dirs_list "$arg" ;;
esac
;; ;;
--rpath=*) --rpath=*)
arg="${1#--rpath=}" arg="${1#--rpath=}"
if [ -z "$arg" ]; then if [ -z "$arg" ]; then
shift; continue shift; continue
fi fi
append_path_lists return_rpath_dirs_list "$arg" path_order "$arg"
case $? in
0) append return_spack_store_rpath_dirs_list "$arg" ;;
1) append return_rpath_dirs_list "$arg" ;;
2) append return_system_rpath_dirs_list "$arg" ;;
esac
;; ;;
-rpath|--rpath) -rpath|--rpath)
wl_expect_rpath=yes wl_expect_rpath=yes
@@ -535,7 +509,8 @@ parse_Wl() {
"$dtags_to_strip") "$dtags_to_strip")
;; ;;
-Wl) -Wl)
# Nested -Wl,-Wl means we're in NAG compiler territory. We don't support it. # Nested -Wl,-Wl means we're in NAG compiler territory, we don't support
# it.
return 1 return 1
;; ;;
*) *)
@@ -554,10 +529,21 @@ categorize_arguments() {
return_other_args_list="" return_other_args_list=""
return_isystem_was_used="" return_isystem_was_used=""
init_path_lists return_isystem_include_dirs_list return_isystem_spack_store_include_dirs_list=""
init_path_lists return_include_dirs_list return_isystem_system_include_dirs_list=""
init_path_lists return_lib_dirs_list return_isystem_include_dirs_list=""
init_path_lists return_rpath_dirs_list
return_spack_store_include_dirs_list=""
return_system_include_dirs_list=""
return_include_dirs_list=""
return_spack_store_lib_dirs_list=""
return_system_lib_dirs_list=""
return_lib_dirs_list=""
return_spack_store_rpath_dirs_list=""
return_system_rpath_dirs_list=""
return_rpath_dirs_list=""
# Global state for keeping track of -Wl,-rpath -Wl,/path # Global state for keeping track of -Wl,-rpath -Wl,/path
wl_expect_rpath=no wl_expect_rpath=no
@@ -623,17 +609,32 @@ categorize_arguments() {
arg="${1#-isystem}" arg="${1#-isystem}"
return_isystem_was_used=true return_isystem_was_used=true
if [ -z "$arg" ]; then shift; arg="$1"; fi if [ -z "$arg" ]; then shift; arg="$1"; fi
append_path_lists return_isystem_include_dirs_list "$arg" path_order "$arg"
case $? in
0) append return_isystem_spack_store_include_dirs_list "$arg" ;;
1) append return_isystem_include_dirs_list "$arg" ;;
2) append return_isystem_system_include_dirs_list "$arg" ;;
esac
;; ;;
-I*) -I*)
arg="${1#-I}" arg="${1#-I}"
if [ -z "$arg" ]; then shift; arg="$1"; fi if [ -z "$arg" ]; then shift; arg="$1"; fi
append_path_lists return_include_dirs_list "$arg" path_order "$arg"
case $? in
0) append return_spack_store_include_dirs_list "$arg" ;;
1) append return_include_dirs_list "$arg" ;;
2) append return_system_include_dirs_list "$arg" ;;
esac
;; ;;
-L*) -L*)
arg="${1#-L}" arg="${1#-L}"
if [ -z "$arg" ]; then shift; arg="$1"; fi if [ -z "$arg" ]; then shift; arg="$1"; fi
append_path_lists return_lib_dirs_list "$arg" path_order "$arg"
case $? in
0) append return_spack_store_lib_dirs_list "$arg" ;;
1) append return_lib_dirs_list "$arg" ;;
2) append return_system_lib_dirs_list "$arg" ;;
esac
;; ;;
-l*) -l*)
# -loopopt=0 is generated erroneously in autoconf <= 2.69, # -loopopt=0 is generated erroneously in autoconf <= 2.69,
@@ -666,17 +667,32 @@ categorize_arguments() {
break break
elif [ "$xlinker_expect_rpath" = yes ]; then elif [ "$xlinker_expect_rpath" = yes ]; then
# Register the path of -Xlinker -rpath <other args> -Xlinker <path> # Register the path of -Xlinker -rpath <other args> -Xlinker <path>
append_path_lists return_rpath_dirs_list "$1" path_order "$1"
case $? in
0) append return_spack_store_rpath_dirs_list "$1" ;;
1) append return_rpath_dirs_list "$1" ;;
2) append return_system_rpath_dirs_list "$1" ;;
esac
xlinker_expect_rpath=no xlinker_expect_rpath=no
else else
case "$1" in case "$1" in
-rpath=*) -rpath=*)
arg="${1#-rpath=}" arg="${1#-rpath=}"
append_path_lists return_rpath_dirs_list "$arg" path_order "$arg"
case $? in
0) append return_spack_store_rpath_dirs_list "$arg" ;;
1) append return_rpath_dirs_list "$arg" ;;
2) append return_system_rpath_dirs_list "$arg" ;;
esac
;; ;;
--rpath=*) --rpath=*)
arg="${1#--rpath=}" arg="${1#--rpath=}"
append_path_lists return_rpath_dirs_list "$arg" path_order "$arg"
case $? in
0) append return_spack_store_rpath_dirs_list "$arg" ;;
1) append return_rpath_dirs_list "$arg" ;;
2) append return_system_rpath_dirs_list "$arg" ;;
esac
;; ;;
-rpath|--rpath) -rpath|--rpath)
xlinker_expect_rpath=yes xlinker_expect_rpath=yes
@@ -693,32 +709,7 @@ categorize_arguments() {
"$dtags_to_strip") "$dtags_to_strip")
;; ;;
*) *)
# if mode is not ld, we can just add to other args append return_other_args_list "$1"
if [ "$mode" != "ld" ]; then
append return_other_args_list "$1"
shift
continue
fi
# if we're in linker mode, we need to parse raw RPATH args
case "$1" in
-rpath=*)
arg="${1#-rpath=}"
append_path_lists return_rpath_dirs_list "$arg"
;;
--rpath=*)
arg="${1#--rpath=}"
append_path_lists return_rpath_dirs_list "$arg"
;;
-rpath|--rpath)
shift
[ $# -eq 0 ] && break # ignore -rpath without value
append_path_lists return_rpath_dirs_list "$1"
;;
*)
append return_other_args_list "$1"
;;
esac
;; ;;
esac esac
shift shift
@@ -740,10 +731,21 @@ categorize_arguments() {
categorize_arguments "$@" categorize_arguments "$@"
assign_path_lists isystem_include_dirs_list return_isystem_include_dirs_list spack_store_include_dirs_list="$return_spack_store_include_dirs_list"
assign_path_lists include_dirs_list return_include_dirs_list system_include_dirs_list="$return_system_include_dirs_list"
assign_path_lists lib_dirs_list return_lib_dirs_list include_dirs_list="$return_include_dirs_list"
assign_path_lists rpath_dirs_list return_rpath_dirs_list
spack_store_lib_dirs_list="$return_spack_store_lib_dirs_list"
system_lib_dirs_list="$return_system_lib_dirs_list"
lib_dirs_list="$return_lib_dirs_list"
spack_store_rpath_dirs_list="$return_spack_store_rpath_dirs_list"
system_rpath_dirs_list="$return_system_rpath_dirs_list"
rpath_dirs_list="$return_rpath_dirs_list"
isystem_spack_store_include_dirs_list="$return_isystem_spack_store_include_dirs_list"
isystem_system_include_dirs_list="$return_isystem_system_include_dirs_list"
isystem_include_dirs_list="$return_isystem_include_dirs_list"
isystem_was_used="$return_isystem_was_used" isystem_was_used="$return_isystem_was_used"
other_args_list="$return_other_args_list" other_args_list="$return_other_args_list"
@@ -819,10 +821,21 @@ IFS="$lsep"
categorize_arguments $spack_flags_list categorize_arguments $spack_flags_list
unset IFS unset IFS
assign_path_lists spack_flags_isystem_include_dirs_list return_isystem_include_dirs_list spack_flags_isystem_spack_store_include_dirs_list="$return_isystem_spack_store_include_dirs_list"
assign_path_lists spack_flags_include_dirs_list return_include_dirs_list spack_flags_isystem_system_include_dirs_list="$return_isystem_system_include_dirs_list"
assign_path_lists spack_flags_lib_dirs_list return_lib_dirs_list spack_flags_isystem_include_dirs_list="$return_isystem_include_dirs_list"
assign_path_lists spack_flags_rpath_dirs_list return_rpath_dirs_list
spack_flags_spack_store_include_dirs_list="$return_spack_store_include_dirs_list"
spack_flags_system_include_dirs_list="$return_system_include_dirs_list"
spack_flags_include_dirs_list="$return_include_dirs_list"
spack_flags_spack_store_lib_dirs_list="$return_spack_store_lib_dirs_list"
spack_flags_system_lib_dirs_list="$return_system_lib_dirs_list"
spack_flags_lib_dirs_list="$return_lib_dirs_list"
spack_flags_spack_store_rpath_dirs_list="$return_spack_store_rpath_dirs_list"
spack_flags_system_rpath_dirs_list="$return_system_rpath_dirs_list"
spack_flags_rpath_dirs_list="$return_rpath_dirs_list"
spack_flags_isystem_was_used="$return_isystem_was_used" spack_flags_isystem_was_used="$return_isystem_was_used"
spack_flags_other_args_list="$return_other_args_list" spack_flags_other_args_list="$return_other_args_list"
@@ -841,11 +854,13 @@ if [ "$mode" = ld ] || [ "$mode" = ccld ]; then
fi fi
if [ "$mode" = ccld ] || [ "$mode" = ld ]; then if [ "$mode" = ccld ] || [ "$mode" = ld ]; then
# Append RPATH directories. Note that in the case of the if [ "$add_rpaths" != "false" ]; then
# top-level package these directories may not exist yet. For dependencies # Append RPATH directories. Note that in the case of the
# it is assumed that paths have already been confirmed. # top-level package these directories may not exist yet. For dependencies
extend spack_store_rpath_dirs_list SPACK_STORE_RPATH_DIRS # it is assumed that paths have already been confirmed.
extend rpath_dirs_list SPACK_RPATH_DIRS extend spack_store_rpath_dirs_list SPACK_STORE_RPATH_DIRS
extend rpath_dirs_list SPACK_RPATH_DIRS
fi
fi fi
if [ "$mode" = ccld ] || [ "$mode" = ld ]; then if [ "$mode" = ccld ] || [ "$mode" = ld ]; then
@@ -860,10 +875,14 @@ case "$mode" in
ld|ccld) ld|ccld)
# Set extra RPATHs # Set extra RPATHs
extend lib_dirs_list SPACK_COMPILER_EXTRA_RPATHS extend lib_dirs_list SPACK_COMPILER_EXTRA_RPATHS
extend rpath_dirs_list SPACK_COMPILER_EXTRA_RPATHS if [ "$add_rpaths" != "false" ]; then
extend rpath_dirs_list SPACK_COMPILER_EXTRA_RPATHS
fi
# Set implicit RPATHs # Set implicit RPATHs
extend rpath_dirs_list SPACK_COMPILER_IMPLICIT_RPATHS if [ "$add_rpaths" != "false" ]; then
extend rpath_dirs_list SPACK_COMPILER_IMPLICIT_RPATHS
fi
# Add SPACK_LDLIBS to args # Add SPACK_LDLIBS to args
for lib in $SPACK_LDLIBS; do for lib in $SPACK_LDLIBS; do
@@ -875,7 +894,7 @@ esac
case "$mode" in case "$mode" in
cpp|cc|as|ccld) cpp|cc|as|ccld)
if [ "$spack_flags_isystem_was_used" = "true" ] || [ "$isystem_was_used" = "true" ]; then if [ "$spack_flags_isystem_was_used" = "true" ] || [ "$isystem_was_used" = "true" ]; then
extend spack_store_isystem_include_dirs_list SPACK_STORE_INCLUDE_DIRS extend isystem_spack_store_include_dirs_list SPACK_STORE_INCLUDE_DIRS
extend isystem_include_dirs_list SPACK_INCLUDE_DIRS extend isystem_include_dirs_list SPACK_INCLUDE_DIRS
else else
extend spack_store_include_dirs_list SPACK_STORE_INCLUDE_DIRS extend spack_store_include_dirs_list SPACK_STORE_INCLUDE_DIRS
@@ -891,76 +910,64 @@ args_list="$flags_list"
# Include search paths partitioned by (in store, non-sytem, system) # Include search paths partitioned by (in store, non-sytem, system)
# NOTE: adding ${lsep} to the prefix here turns every added element into two # NOTE: adding ${lsep} to the prefix here turns every added element into two
extend args_list spack_store_spack_flags_include_dirs_list -I extend args_list spack_flags_spack_store_include_dirs_list -I
extend args_list spack_store_include_dirs_list -I extend args_list spack_store_include_dirs_list -I
extend args_list spack_flags_include_dirs_list -I extend args_list spack_flags_include_dirs_list -I
extend args_list include_dirs_list -I extend args_list include_dirs_list -I
extend args_list spack_store_spack_flags_isystem_include_dirs_list "-isystem${lsep}" extend args_list spack_flags_isystem_spack_store_include_dirs_list "-isystem${lsep}"
extend args_list spack_store_isystem_include_dirs_list "-isystem${lsep}" extend args_list isystem_spack_store_include_dirs_list "-isystem${lsep}"
extend args_list spack_flags_isystem_include_dirs_list "-isystem${lsep}" extend args_list spack_flags_isystem_include_dirs_list "-isystem${lsep}"
extend args_list isystem_include_dirs_list "-isystem${lsep}" extend args_list isystem_include_dirs_list "-isystem${lsep}"
extend args_list system_spack_flags_include_dirs_list -I extend args_list spack_flags_system_include_dirs_list -I
extend args_list system_include_dirs_list -I extend args_list system_include_dirs_list -I
extend args_list system_spack_flags_isystem_include_dirs_list "-isystem${lsep}" extend args_list spack_flags_isystem_system_include_dirs_list "-isystem${lsep}"
extend args_list system_isystem_include_dirs_list "-isystem${lsep}" extend args_list isystem_system_include_dirs_list "-isystem${lsep}"
# Library search paths partitioned by (in store, non-sytem, system) # Library search paths partitioned by (in store, non-sytem, system)
extend args_list spack_store_spack_flags_lib_dirs_list "-L" extend args_list spack_flags_spack_store_lib_dirs_list "-L"
extend args_list spack_store_lib_dirs_list "-L" extend args_list spack_store_lib_dirs_list "-L"
extend args_list spack_flags_lib_dirs_list "-L" extend args_list spack_flags_lib_dirs_list "-L"
extend args_list lib_dirs_list "-L" extend args_list lib_dirs_list "-L"
extend args_list system_spack_flags_lib_dirs_list "-L" extend args_list spack_flags_system_lib_dirs_list "-L"
extend args_list system_lib_dirs_list "-L" extend args_list system_lib_dirs_list "-L"
# RPATH arguments # RPATHs arguments
rpath_prefix=""
case "$mode" in case "$mode" in
ccld) ccld)
if [ -n "$dtags_to_add" ] ; then if [ -n "$dtags_to_add" ] ; then
append args_list "$linker_arg$dtags_to_add" append args_list "$linker_arg$dtags_to_add"
fi fi
rpath_prefix="$rpath" extend args_list spack_flags_spack_store_rpath_dirs_list "$rpath"
extend args_list spack_store_rpath_dirs_list "$rpath"
extend args_list spack_flags_rpath_dirs_list "$rpath"
extend args_list rpath_dirs_list "$rpath"
extend args_list spack_flags_system_rpath_dirs_list "$rpath"
extend args_list system_rpath_dirs_list "$rpath"
;; ;;
ld) ld)
if [ -n "$dtags_to_add" ] ; then if [ -n "$dtags_to_add" ] ; then
append args_list "$dtags_to_add" append args_list "$dtags_to_add"
fi fi
rpath_prefix="-rpath${lsep}" extend args_list spack_flags_spack_store_rpath_dirs_list "-rpath${lsep}"
extend args_list spack_store_rpath_dirs_list "-rpath${lsep}"
extend args_list spack_flags_rpath_dirs_list "-rpath${lsep}"
extend args_list rpath_dirs_list "-rpath${lsep}"
extend args_list spack_flags_system_rpath_dirs_list "-rpath${lsep}"
extend args_list system_rpath_dirs_list "-rpath${lsep}"
;; ;;
esac esac
# Darwin's linker has a -r argument that merges object files together.
# It doesn't work with -rpath. add_rpaths controls whether RPATHs are added.
add_rpaths=true
if [ "$mode" = ld ] || [ "$mode" = ccld ]; then
if [ "${SPACK_SHORT_SPEC#*darwin}" != "${SPACK_SHORT_SPEC}" ]; then
args="$@"
if contains args "-r" || contains args "-Wl,-r"; then
add_rpaths=false
fi
fi
fi
# if mode is ccld or ld, extend RPATH lists, adding the prefix determined above
if [ "$add_rpaths" == "true" ] && [ -n "$rpath_prefix" ]; then
extend_unique args_list spack_store_spack_flags_rpath_dirs_list "$rpath_prefix"
extend_unique args_list spack_store_rpath_dirs_list "$rpath_prefix"
extend_unique args_list spack_flags_rpath_dirs_list "$rpath_prefix"
extend_unique args_list rpath_dirs_list "$rpath_prefix"
extend_unique args_list system_spack_flags_rpath_dirs_list "$rpath_prefix"
extend_unique args_list system_rpath_dirs_list "$rpath_prefix"
fi
# Other arguments from the input command # Other arguments from the input command
extend args_list other_args_list extend args_list other_args_list
extend args_list spack_flags_other_args_list extend args_list spack_flags_other_args_list

View File

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

View File

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

View File

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

View File

@@ -47,11 +47,7 @@ def decorator(factory):
def partial_uarch( def partial_uarch(
name: str = "", name: str = "", vendor: str = "", features: Optional[Set[str]] = None, generation: int = 0
vendor: str = "",
features: Optional[Set[str]] = None,
generation: int = 0,
cpu_part: str = "",
) -> Microarchitecture: ) -> Microarchitecture:
"""Construct a partial microarchitecture, from information gathered during system scan.""" """Construct a partial microarchitecture, from information gathered during system scan."""
return Microarchitecture( return Microarchitecture(
@@ -61,7 +57,6 @@ def partial_uarch(
features=features or set(), features=features or set(),
compilers={}, compilers={},
generation=generation, generation=generation,
cpu_part=cpu_part,
) )
@@ -95,7 +90,6 @@ def proc_cpuinfo() -> Microarchitecture:
return partial_uarch( return partial_uarch(
vendor=_canonicalize_aarch64_vendor(data), vendor=_canonicalize_aarch64_vendor(data),
features=_feature_set(data, key="Features"), features=_feature_set(data, key="Features"),
cpu_part=data.get("CPU part", ""),
) )
if architecture in (PPC64LE, PPC64): if architecture in (PPC64LE, PPC64):
@@ -351,10 +345,6 @@ def sorting_fn(item):
generic_candidates = [c for c in candidates if c.vendor == "generic"] generic_candidates = [c for c in candidates if c.vendor == "generic"]
best_generic = max(generic_candidates, key=sorting_fn) best_generic = max(generic_candidates, key=sorting_fn)
# Relevant for AArch64. Filter on "cpu_part" if we have any match
if info.cpu_part != "" and any(c for c in candidates if info.cpu_part == c.cpu_part):
candidates = [c for c in candidates if info.cpu_part == c.cpu_part]
# Filter the candidates to be descendant of the best generic candidate. # Filter the candidates to be descendant of the best generic candidate.
# This is to avoid that the lack of a niche feature that can be disabled # This is to avoid that the lack of a niche feature that can be disabled
# from e.g. BIOS prevents detection of a reasonably performant architecture # from e.g. BIOS prevents detection of a reasonably performant architecture

View File

@@ -2,7 +2,9 @@
# Archspec Project Developers. See the top-level COPYRIGHT file for details. # Archspec Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Types and functions to manage information on CPU microarchitectures.""" """Types and functions to manage information
on CPU microarchitectures.
"""
import functools import functools
import platform import platform
import re import re
@@ -63,24 +65,21 @@ class Microarchitecture:
passed in as argument above. passed in as argument above.
* versions: versions that support this micro-architecture. * versions: versions that support this micro-architecture.
generation (int): generation of the micro-architecture, if relevant. generation (int): generation of the micro-architecture, if
cpu_part (str): cpu part of the architecture, if relevant. relevant.
""" """
# pylint: disable=too-many-arguments,too-many-instance-attributes # pylint: disable=too-many-arguments
#: Aliases for micro-architecture's features #: Aliases for micro-architecture's features
feature_aliases = FEATURE_ALIASES feature_aliases = FEATURE_ALIASES
def __init__(self, name, parents, vendor, features, compilers, generation=0, cpu_part=""): def __init__(self, name, parents, vendor, features, compilers, generation=0):
self.name = name self.name = name
self.parents = parents self.parents = parents
self.vendor = vendor self.vendor = vendor
self.features = features self.features = features
self.compilers = compilers self.compilers = compilers
# Only relevant for PowerPC
self.generation = generation self.generation = generation
# Only relevant for AArch64
self.cpu_part = cpu_part
# Cache the ancestor computation # Cache the ancestor computation
self._ancestors = None self._ancestors = None
@@ -112,12 +111,8 @@ def __eq__(self, other):
and self.parents == other.parents # avoid ancestors here and self.parents == other.parents # avoid ancestors here
and self.compilers == other.compilers and self.compilers == other.compilers
and self.generation == other.generation and self.generation == other.generation
and self.cpu_part == other.cpu_part
) )
def __hash__(self):
return hash(self.name)
@coerce_target_names @coerce_target_names
def __ne__(self, other): def __ne__(self, other):
return not self == other return not self == other
@@ -148,8 +143,7 @@ def __repr__(self):
cls_name = self.__class__.__name__ cls_name = self.__class__.__name__
fmt = ( fmt = (
cls_name + "({0.name!r}, {0.parents!r}, {0.vendor!r}, " cls_name + "({0.name!r}, {0.parents!r}, {0.vendor!r}, "
"{0.features!r}, {0.compilers!r}, generation={0.generation!r}, " "{0.features!r}, {0.compilers!r}, {0.generation!r})"
"cpu_part={0.cpu_part!r})"
) )
return fmt.format(self) return fmt.format(self)
@@ -196,7 +190,6 @@ def to_dict(self):
"generation": self.generation, "generation": self.generation,
"parents": [str(x) for x in self.parents], "parents": [str(x) for x in self.parents],
"compilers": self.compilers, "compilers": self.compilers,
"cpupart": self.cpu_part,
} }
@staticmethod @staticmethod
@@ -209,7 +202,6 @@ def from_dict(data) -> "Microarchitecture":
features=set(data["features"]), features=set(data["features"]),
compilers=data.get("compilers", {}), compilers=data.get("compilers", {}),
generation=data.get("generation", 0), generation=data.get("generation", 0),
cpu_part=data.get("cpupart", ""),
) )
def optimization_flags(self, compiler, version): def optimization_flags(self, compiler, version):
@@ -368,11 +360,8 @@ def fill_target_from_dict(name, data, targets):
features = set(values["features"]) features = set(values["features"])
compilers = values.get("compilers", {}) compilers = values.get("compilers", {})
generation = values.get("generation", 0) generation = values.get("generation", 0)
cpu_part = values.get("cpupart", "")
targets[name] = Microarchitecture( targets[name] = Microarchitecture(name, parents, vendor, features, compilers, generation)
name, parents, vendor, features, compilers, generation=generation, cpu_part=cpu_part
)
known_targets = {} known_targets = {}
data = archspec.cpu.schema.TARGETS_JSON["microarchitectures"] data = archspec.cpu.schema.TARGETS_JSON["microarchitectures"]

View File

@@ -2225,14 +2225,10 @@
], ],
"nvhpc": [ "nvhpc": [
{ {
"versions": "21.11:23.8", "versions": "21.11:",
"name": "zen3", "name": "zen3",
"flags": "-tp {name}", "flags": "-tp {name}",
"warnings": "zen4 is not fully supported by nvhpc versions < 23.9, falling back to zen3" "warnings": "zen4 is not fully supported by nvhpc yet, falling back to zen3"
},
{
"versions": "23.9:",
"flags": "-tp {name}"
} }
] ]
} }
@@ -2715,8 +2711,7 @@
"flags": "-mcpu=thunderx2t99" "flags": "-mcpu=thunderx2t99"
} }
] ]
}, }
"cpupart": "0x0af"
}, },
"a64fx": { "a64fx": {
"from": ["armv8.2a"], "from": ["armv8.2a"],
@@ -2784,8 +2779,7 @@
"flags": "-march=armv8.2-a+crc+crypto+fp16+sve" "flags": "-march=armv8.2-a+crc+crypto+fp16+sve"
} }
] ]
}, }
"cpupart": "0x001"
}, },
"cortex_a72": { "cortex_a72": {
"from": ["aarch64"], "from": ["aarch64"],
@@ -2822,8 +2816,7 @@
"flags" : "-mcpu=cortex-a72" "flags" : "-mcpu=cortex-a72"
} }
] ]
}, }
"cpupart": "0xd08"
}, },
"neoverse_n1": { "neoverse_n1": {
"from": ["cortex_a72", "armv8.2a"], "from": ["cortex_a72", "armv8.2a"],
@@ -2844,7 +2837,8 @@
"asimdrdm", "asimdrdm",
"lrcpc", "lrcpc",
"dcpop", "dcpop",
"asimddp" "asimddp",
"ssbs"
], ],
"compilers" : { "compilers" : {
"gcc": [ "gcc": [
@@ -2908,8 +2902,7 @@
"flags": "-tp {name}" "flags": "-tp {name}"
} }
] ]
}, }
"cpupart": "0xd0c"
}, },
"neoverse_v1": { "neoverse_v1": {
"from": ["neoverse_n1", "armv8.4a"], "from": ["neoverse_n1", "armv8.4a"],
@@ -2933,6 +2926,8 @@
"lrcpc", "lrcpc",
"dcpop", "dcpop",
"sha3", "sha3",
"sm3",
"sm4",
"asimddp", "asimddp",
"sha512", "sha512",
"sve", "sve",
@@ -2941,6 +2936,7 @@
"uscat", "uscat",
"ilrcpc", "ilrcpc",
"flagm", "flagm",
"ssbs",
"dcpodp", "dcpodp",
"svei8mm", "svei8mm",
"svebf16", "svebf16",
@@ -3008,7 +3004,7 @@
}, },
{ {
"versions": "11:", "versions": "11:",
"flags" : "-march=armv8.4-a+sve+fp16+bf16+crypto+i8mm+rng" "flags" : "-march=armv8.4-a+sve+ssbs+fp16+bf16+crypto+i8mm+rng"
}, },
{ {
"versions": "12:", "versions": "12:",
@@ -3032,8 +3028,7 @@
"flags": "-tp {name}" "flags": "-tp {name}"
} }
] ]
}, }
"cpupart": "0xd40"
}, },
"neoverse_v2": { "neoverse_v2": {
"from": ["neoverse_n1", "armv9.0a"], "from": ["neoverse_n1", "armv9.0a"],
@@ -3057,22 +3052,32 @@
"lrcpc", "lrcpc",
"dcpop", "dcpop",
"sha3", "sha3",
"sm3",
"sm4",
"asimddp", "asimddp",
"sha512", "sha512",
"sve", "sve",
"asimdfhm", "asimdfhm",
"dit",
"uscat", "uscat",
"ilrcpc", "ilrcpc",
"flagm", "flagm",
"ssbs",
"sb", "sb",
"dcpodp", "dcpodp",
"sve2", "sve2",
"sveaes",
"svepmull",
"svebitperm",
"svesha3",
"svesm4",
"flagm2", "flagm2",
"frint", "frint",
"svei8mm", "svei8mm",
"svebf16", "svebf16",
"i8mm", "i8mm",
"bf16" "bf16",
"dgh"
], ],
"compilers" : { "compilers" : {
"gcc": [ "gcc": [
@@ -3097,19 +3102,15 @@
"flags" : "-march=armv8.5-a+sve -mtune=cortex-a76" "flags" : "-march=armv8.5-a+sve -mtune=cortex-a76"
}, },
{ {
"versions": "10.0:11.3.99", "versions": "10.0:11.99",
"flags" : "-march=armv8.5-a+sve+sve2+i8mm+bf16 -mtune=cortex-a77" "flags" : "-march=armv8.5-a+sve+sve2+i8mm+bf16 -mtune=cortex-a77"
}, },
{
"versions": "11.4:11.99",
"flags" : "-mcpu=neoverse-v2"
},
{ {
"versions": "12.0:12.2.99", "versions": "12.0:12.99",
"flags" : "-march=armv9-a+i8mm+bf16 -mtune=cortex-a710" "flags" : "-march=armv9-a+i8mm+bf16 -mtune=cortex-a710"
}, },
{ {
"versions": "12.3:", "versions": "13.0:",
"flags" : "-mcpu=neoverse-v2" "flags" : "-mcpu=neoverse-v2"
} }
], ],
@@ -3144,112 +3145,7 @@
"flags": "-tp {name}" "flags": "-tp {name}"
} }
] ]
}, }
"cpupart": "0xd4f"
},
"neoverse_n2": {
"from": ["neoverse_n1", "armv9.0a"],
"vendor": "ARM",
"features": [
"fp",
"asimd",
"evtstrm",
"aes",
"pmull",
"sha1",
"sha2",
"crc32",
"atomics",
"fphp",
"asimdhp",
"cpuid",
"asimdrdm",
"jscvt",
"fcma",
"lrcpc",
"dcpop",
"sha3",
"asimddp",
"sha512",
"sve",
"asimdfhm",
"uscat",
"ilrcpc",
"flagm",
"sb",
"dcpodp",
"sve2",
"flagm2",
"frint",
"svei8mm",
"svebf16",
"i8mm",
"bf16"
],
"compilers" : {
"gcc": [
{
"versions": "4.8:5.99",
"flags": "-march=armv8-a"
},
{
"versions": "6:6.99",
"flags" : "-march=armv8.1-a"
},
{
"versions": "7.0:7.99",
"flags" : "-march=armv8.2-a -mtune=cortex-a72"
},
{
"versions": "8.0:8.99",
"flags" : "-march=armv8.4-a+sve -mtune=cortex-a72"
},
{
"versions": "9.0:9.99",
"flags" : "-march=armv8.5-a+sve -mtune=cortex-a76"
},
{
"versions": "10.0:10.99",
"flags" : "-march=armv8.5-a+sve+sve2+i8mm+bf16 -mtune=cortex-a77"
},
{
"versions": "11.0:",
"flags" : "-mcpu=neoverse-n2"
}
],
"clang" : [
{
"versions": "9.0:10.99",
"flags" : "-march=armv8.5-a+sve"
},
{
"versions": "11.0:13.99",
"flags" : "-march=armv8.5-a+sve+sve2+i8mm+bf16"
},
{
"versions": "14.0:15.99",
"flags" : "-march=armv9-a+i8mm+bf16"
},
{
"versions": "16.0:",
"flags" : "-mcpu=neoverse-n2"
}
],
"arm" : [
{
"versions": "23.04.0:",
"flags" : "-mcpu=neoverse-n2"
}
],
"nvhpc" : [
{
"versions": "23.3:",
"name": "neoverse-n1",
"flags": "-tp {name}"
}
]
},
"cpupart": "0xd49"
}, },
"m1": { "m1": {
"from": ["armv8.4a"], "from": ["armv8.4a"],
@@ -3315,8 +3211,7 @@
"flags" : "-mcpu=apple-m1" "flags" : "-mcpu=apple-m1"
} }
] ]
}, }
"cpupart": "0x022"
}, },
"m2": { "m2": {
"from": ["m1", "armv8.5a"], "from": ["m1", "armv8.5a"],
@@ -3394,8 +3289,7 @@
"flags" : "-mcpu=apple-m2" "flags" : "-mcpu=apple-m2"
} }
] ]
}, }
"cpupart": "0x032"
}, },
"arm": { "arm": {
"from": [], "from": [],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,13 +3,6 @@
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import re
from typing import Optional
import spack.paths
import spack.util.git
#: PEP440 canonical <major>.<minor>.<micro>.<devN> string #: PEP440 canonical <major>.<minor>.<micro>.<devN> string
__version__ = "0.23.0.dev0" __version__ = "0.23.0.dev0"
spack_version = __version__ spack_version = __version__
@@ -26,47 +19,4 @@ def __try_int(v):
spack_version_info = tuple([__try_int(v) for v in __version__.split(".")]) spack_version_info = tuple([__try_int(v) for v in __version__.split(".")])
def get_spack_commit() -> Optional[str]: __all__ = ["spack_version_info", "spack_version"]
"""Get the Spack git commit sha.
Returns:
(str or None) the commit sha if available, otherwise None
"""
git_path = os.path.join(spack.paths.prefix, ".git")
if not os.path.exists(git_path):
return None
git = spack.util.git.git()
if not git:
return None
rev = git(
"-C",
spack.paths.prefix,
"rev-parse",
"HEAD",
output=str,
error=os.devnull,
fail_on_error=False,
)
if git.returncode != 0:
return None
match = re.match(r"[a-f\d]{7,}$", rev)
return match.group(0) if match else None
def get_version() -> str:
"""Get a descriptive version of this instance of Spack.
Outputs '<PEP440 version> (<git commit sha>)'.
The commit sha is only added when available.
"""
commit = get_spack_commit()
if commit:
return f"{spack_version} ({commit})"
return spack_version
__all__ = ["spack_version_info", "spack_version", "get_version", "get_spack_commit"]

131
lib/spack/spack/abi.py Normal file
View File

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

View File

@@ -39,21 +39,18 @@ def _search_duplicate_compilers(error_cls):
import collections import collections
import collections.abc import collections.abc
import glob import glob
import inspect
import io import io
import itertools import itertools
import os
import pathlib import pathlib
import pickle import pickle
import re import re
import warnings import warnings
from typing import Iterable, List, Set, Tuple
from urllib.request import urlopen from urllib.request import urlopen
import llnl.util.lang import llnl.util.lang
import spack.builder
import spack.config import spack.config
import spack.fetch_strategy
import spack.patch import spack.patch
import spack.repo import spack.repo
import spack.spec import spack.spec
@@ -76,9 +73,7 @@ def __init__(self, summary, details):
self.details = tuple(details) self.details = tuple(details)
def __str__(self): def __str__(self):
if self.details: return self.summary + "\n" + "\n".join([" " + detail for detail in self.details])
return f"{self.summary}\n" + "\n".join(f" {detail}" for detail in self.details)
return self.summary
def __eq__(self, other): def __eq__(self, other):
if self.summary != other.summary or self.details != other.details: if self.summary != other.summary or self.details != other.details:
@@ -215,11 +210,6 @@ def _search_duplicate_compilers(error_cls):
group="configs", tag="CFG-PACKAGES", description="Sanity checks on packages.yaml", kwargs=() group="configs", tag="CFG-PACKAGES", description="Sanity checks on packages.yaml", kwargs=()
) )
#: Sanity checks on packages.yaml
config_repos = AuditClass(
group="configs", tag="CFG-REPOS", description="Sanity checks on repositories", kwargs=()
)
@config_packages @config_packages
def _search_duplicate_specs_in_externals(error_cls): def _search_duplicate_specs_in_externals(error_cls):
@@ -262,6 +252,40 @@ def _search_duplicate_specs_in_externals(error_cls):
return errors return errors
@config_packages
def _deprecated_preferences(error_cls):
"""Search package preferences deprecated in v0.21 (and slated for removal in v0.23)"""
# TODO (v0.23): remove this audit as the attributes will not be allowed in config
errors = []
packages_yaml = spack.config.CONFIG.get_config("packages")
def make_error(attribute_name, config_data, summary):
s = io.StringIO()
s.write("Occurring in the following file:\n")
dict_view = syaml.syaml_dict((k, v) for k, v in config_data.items() if k == attribute_name)
syaml.dump_config(dict_view, stream=s, blame=True)
return error_cls(summary=summary, details=[s.getvalue()])
if "all" in packages_yaml and "version" in packages_yaml["all"]:
summary = "Using the deprecated 'version' attribute under 'packages:all'"
errors.append(make_error("version", packages_yaml["all"], summary))
for package_name in packages_yaml:
if package_name == "all":
continue
package_conf = packages_yaml[package_name]
for attribute in ("compiler", "providers", "target"):
if attribute not in package_conf:
continue
summary = (
f"Using the deprecated '{attribute}' attribute " f"under 'packages:{package_name}'"
)
errors.append(make_error(attribute, package_conf, summary))
return errors
@config_packages @config_packages
def _avoid_mismatched_variants(error_cls): def _avoid_mismatched_variants(error_cls):
"""Warns if variant preferences have mismatched types or names.""" """Warns if variant preferences have mismatched types or names."""
@@ -282,7 +306,7 @@ def _avoid_mismatched_variants(error_cls):
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name) pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
for variant in current_spec.variants.values(): for variant in current_spec.variants.values():
# Variant does not exist at all # Variant does not exist at all
if variant.name not in pkg_cls.variant_names(): if variant.name not in pkg_cls.variants:
summary = ( summary = (
f"Setting a preference for the '{pkg_name}' package to the " f"Setting a preference for the '{pkg_name}' package to the "
f"non-existing variant '{variant.name}'" f"non-existing variant '{variant.name}'"
@@ -291,8 +315,9 @@ def _avoid_mismatched_variants(error_cls):
continue continue
# Variant cannot accept this value # Variant cannot accept this value
s = spack.spec.Spec(pkg_name)
try: try:
spack.variant.prevalidate_variant_value(pkg_cls, variant, strict=True) s.update_variant_validate(variant.name, variant.value)
except Exception: except Exception:
summary = ( summary = (
f"Setting the variant '{variant.name}' of the '{pkg_name}' package " f"Setting the variant '{variant.name}' of the '{pkg_name}' package "
@@ -342,27 +367,6 @@ def _ensure_all_virtual_packages_have_default_providers(error_cls):
] ]
@config_repos
def _ensure_no_folders_without_package_py(error_cls):
"""Check that we don't leave any folder without a package.py in repos"""
errors = []
for repository in spack.repo.PATH.repos:
missing = []
for entry in os.scandir(repository.packages_path):
if not entry.is_dir():
continue
package_py = pathlib.Path(entry.path) / spack.repo.package_file_name
if not package_py.exists():
missing.append(entry.path)
if missing:
summary = (
f"The '{repository.namespace}' repository misses a package.py file"
f" in the following folders"
)
errors.append(error_cls(summary=summary, details=[f"{x}" for x in missing]))
return errors
def _make_config_error(config_data, summary, error_cls): def _make_config_error(config_data, summary, error_cls):
s = io.StringIO() s = io.StringIO()
s.write("Occurring in the following file:\n") s.write("Occurring in the following file:\n")
@@ -494,7 +498,7 @@ def _search_for_reserved_attributes_names_in_packages(pkgs, error_cls):
name_definitions = collections.defaultdict(list) name_definitions = collections.defaultdict(list)
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name) pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
for cls_item in pkg_cls.__mro__: for cls_item in inspect.getmro(pkg_cls):
for name in RESERVED_NAMES: for name in RESERVED_NAMES:
current_value = cls_item.__dict__.get(name) current_value = cls_item.__dict__.get(name)
if current_value is None: if current_value is None:
@@ -523,7 +527,7 @@ def _ensure_all_package_names_are_lowercase(pkgs, error_cls):
badname_regex, errors = re.compile(r"[_A-Z]"), [] badname_regex, errors = re.compile(r"[_A-Z]"), []
for pkg_name in pkgs: for pkg_name in pkgs:
if badname_regex.search(pkg_name): if badname_regex.search(pkg_name):
error_msg = f"Package name '{pkg_name}' should be lowercase and must not contain '_'" error_msg = "Package name '{}' is either lowercase or conatine '_'".format(pkg_name)
errors.append(error_cls(error_msg, [])) errors.append(error_cls(error_msg, []))
return errors return errors
@@ -662,15 +666,9 @@ def _ensure_env_methods_are_ported_to_builders(pkgs, error_cls):
errors = [] errors = []
for pkg_name in pkgs: for pkg_name in pkgs:
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name) pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
buildsystem_variant, _ = pkg_cls.variants["build_system"]
# values are either Value objects (for conditional values) or the values themselves buildsystem_names = [getattr(x, "value", x) for x in buildsystem_variant.values]
build_system_names = set( builder_cls_names = [spack.builder.BUILDER_CLS[x].__name__ for x in buildsystem_names]
v.value if isinstance(v, spack.variant.Value) else v
for _, variant in pkg_cls.variant_definitions("build_system")
for v in variant.values
)
builder_cls_names = [spack.builder.BUILDER_CLS[x].__name__ for x in build_system_names]
module = pkg_cls.module module = pkg_cls.module
has_builders_in_package_py = any( has_builders_in_package_py = any(
getattr(module, name, False) for name in builder_cls_names getattr(module, name, False) for name in builder_cls_names
@@ -689,88 +687,6 @@ def _ensure_env_methods_are_ported_to_builders(pkgs, error_cls):
return errors return errors
class DeprecatedMagicGlobals(ast.NodeVisitor):
def __init__(self, magic_globals: Iterable[str]):
super().__init__()
self.magic_globals: Set[str] = set(magic_globals)
# State to track whether we're in a class function
self.depth: int = 0
self.in_function: bool = False
self.path = (ast.Module, ast.ClassDef, ast.FunctionDef)
# Defined locals in the current function (heuristically at least)
self.locals: Set[str] = set()
# List of (name, lineno) tuples for references to magic globals
self.references_to_globals: List[Tuple[str, int]] = []
def descend_in_function_def(self, node: ast.AST) -> None:
if not isinstance(node, self.path[self.depth]):
return
self.depth += 1
if self.depth == len(self.path):
self.in_function = True
super().generic_visit(node)
if self.depth == len(self.path):
self.in_function = False
self.locals.clear()
self.depth -= 1
def generic_visit(self, node: ast.AST) -> None:
# Recurse into function definitions
if self.depth < len(self.path):
return self.descend_in_function_def(node)
elif not self.in_function:
return
elif isinstance(node, ast.Global):
for name in node.names:
if name in self.magic_globals:
self.references_to_globals.append((name, node.lineno))
elif isinstance(node, ast.Assign):
# visit the rhs before lhs
super().visit(node.value)
for target in node.targets:
super().visit(target)
elif isinstance(node, ast.Name) and node.id in self.magic_globals:
if isinstance(node.ctx, ast.Load) and node.id not in self.locals:
self.references_to_globals.append((node.id, node.lineno))
elif isinstance(node.ctx, ast.Store):
self.locals.add(node.id)
else:
super().generic_visit(node)
@package_properties
def _uses_deprecated_globals(pkgs, error_cls):
"""Ensure that packages do not use deprecated globals"""
errors = []
for pkg_name in pkgs:
# some packages scheduled to be removed in v0.23 are not worth fixing.
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
if all(v.get("deprecated", False) for v in pkg_cls.versions.values()):
continue
file = spack.repo.PATH.filename_for_package_name(pkg_name)
tree = ast.parse(open(file).read())
visitor = DeprecatedMagicGlobals(("std_cmake_args",))
visitor.visit(tree)
if visitor.references_to_globals:
errors.append(
error_cls(
f"Package '{pkg_name}' uses deprecated globals",
[
f"{file}:{line} references '{name}'"
for name, line in visitor.references_to_globals
],
)
)
return errors
@package_https_directives @package_https_directives
def _linting_package_file(pkgs, error_cls): def _linting_package_file(pkgs, error_cls):
"""Check for correctness of links""" """Check for correctness of links"""
@@ -937,22 +853,20 @@ def check_virtual_with_variants(spec, msg):
# check variants # check variants
dependency_variants = dep.spec.variants dependency_variants = dep.spec.variants
for name, variant in dependency_variants.items(): for name, value in dependency_variants.items():
try: try:
spack.variant.prevalidate_variant_value( v, _ = dependency_pkg_cls.variants[name]
dependency_pkg_cls, variant, dep.spec, strict=True v.validate_or_raise(value, pkg_cls=dependency_pkg_cls)
)
except Exception as e: except Exception as e:
summary = ( summary = (
f"{pkg_name}: wrong variant used for dependency in 'depends_on()'" f"{pkg_name}: wrong variant used for dependency in 'depends_on()'"
) )
error_msg = str(e)
if isinstance(e, KeyError): if isinstance(e, KeyError):
error_msg = ( error_msg = (
f"variant {str(e).strip()} does not exist in package {dep_name}" f"variant {str(e).strip()} does not exist in package {dep_name}"
f" in package '{dep_name}'"
) )
error_msg += f" in package '{dep_name}'"
errors.append( errors.append(
error_cls(summary=summary, details=[error_msg, f"in {filename}"]) error_cls(summary=summary, details=[error_msg, f"in {filename}"])
@@ -964,38 +878,39 @@ def check_virtual_with_variants(spec, msg):
@package_directives @package_directives
def _ensure_variant_defaults_are_parsable(pkgs, error_cls): def _ensure_variant_defaults_are_parsable(pkgs, error_cls):
"""Ensures that variant defaults are present and parsable from cli""" """Ensures that variant defaults are present and parsable from cli"""
def check_variant(pkg_cls, variant, vname):
# bool is a subclass of int in python. Permitting a default that is an instance
# of 'int' means both foo=false and foo=0 are accepted. Other falsish values are
# not allowed, since they can't be parsed from CLI ('foo=')
default_is_parsable = isinstance(variant.default, int) or variant.default
if not default_is_parsable:
msg = f"Variant '{vname}' of package '{pkg_cls.name}' has an unparsable default value"
return [error_cls(msg, [])]
try:
vspec = variant.make_default()
except spack.variant.MultipleValuesInExclusiveVariantError:
msg = f"Can't create default value for variant '{vname}' in package '{pkg_cls.name}'"
return [error_cls(msg, [])]
try:
variant.validate_or_raise(vspec, pkg_cls.name)
except spack.variant.InvalidVariantValueError:
msg = "Default value of variant '{vname}' in package '{pkg.name}' is invalid"
question = "Is it among the allowed values?"
return [error_cls(msg, [question])]
return []
errors = [] errors = []
for pkg_name in pkgs: for pkg_name in pkgs:
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name) pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
for vname in pkg_cls.variant_names(): for variant_name, entry in pkg_cls.variants.items():
for _, variant_def in pkg_cls.variant_definitions(vname): variant, _ = entry
errors.extend(check_variant(pkg_cls, variant_def, vname)) default_is_parsable = (
# Permitting a default that is an instance on 'int' permits
# to have foo=false or foo=0. Other falsish values are
# not allowed, since they can't be parsed from cli ('foo=')
isinstance(variant.default, int)
or variant.default
)
if not default_is_parsable:
error_msg = "Variant '{}' of package '{}' has a bad default value"
errors.append(error_cls(error_msg.format(variant_name, pkg_name), []))
continue
try:
vspec = variant.make_default()
except spack.variant.MultipleValuesInExclusiveVariantError:
error_msg = "Cannot create a default value for the variant '{}' in package '{}'"
errors.append(error_cls(error_msg.format(variant_name, pkg_name), []))
continue
try:
variant.validate_or_raise(vspec, pkg_cls=pkg_cls)
except spack.variant.InvalidVariantValueError:
error_msg = (
"The default value of the variant '{}' in package '{}' failed validation"
)
question = "Is it among the allowed values?"
errors.append(error_cls(error_msg.format(variant_name, pkg_name), [question]))
return errors return errors
@@ -1005,11 +920,11 @@ def _ensure_variants_have_descriptions(pkgs, error_cls):
errors = [] errors = []
for pkg_name in pkgs: for pkg_name in pkgs:
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name) pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
for name in pkg_cls.variant_names(): for variant_name, entry in pkg_cls.variants.items():
for when, variant in pkg_cls.variant_definitions(name): variant, _ = entry
if not variant.description: if not variant.description:
msg = f"Variant '{name}' in package '{pkg_name}' is missing a description" error_msg = "Variant '{}' in package '{}' is missing a description"
errors.append(error_cls(msg, [])) errors.append(error_cls(error_msg.format(variant_name, pkg_name), []))
return errors return errors
@@ -1066,26 +981,29 @@ def _version_constraints_are_satisfiable_by_some_version_in_repo(pkgs, error_cls
def _analyze_variants_in_directive(pkg, constraint, directive, error_cls): def _analyze_variants_in_directive(pkg, constraint, directive, error_cls):
variant_exceptions = (
spack.variant.InconsistentValidationError,
spack.variant.MultipleValuesInExclusiveVariantError,
spack.variant.InvalidVariantValueError,
KeyError,
)
errors = [] errors = []
variant_names = pkg.variant_names()
summary = f"{pkg.name}: wrong variant in '{directive}' directive"
filename = spack.repo.PATH.filename_for_package_name(pkg.name)
for name, v in constraint.variants.items(): for name, v in constraint.variants.items():
if name not in variant_names:
msg = f"variant {name} does not exist in {pkg.name}"
errors.append(error_cls(summary=summary, details=[msg, f"in {filename}"]))
continue
try: try:
spack.variant.prevalidate_variant_value(pkg, v, constraint, strict=True) variant, _ = pkg.variants[name]
except ( variant.validate_or_raise(v, pkg_cls=pkg)
spack.variant.InconsistentValidationError, except variant_exceptions as e:
spack.variant.MultipleValuesInExclusiveVariantError, summary = pkg.name + ': wrong variant in "{0}" directive'
spack.variant.InvalidVariantValueError, summary = summary.format(directive)
) as e: filename = spack.repo.PATH.filename_for_package_name(pkg.name)
msg = str(e).strip()
errors.append(error_cls(summary=summary, details=[msg, f"in {filename}"])) error_msg = str(e).strip()
if isinstance(e, KeyError):
error_msg = "the variant {0} does not exist".format(error_msg)
err = error_cls(summary=summary, details=[error_msg, "in " + filename])
errors.append(err)
return errors return errors
@@ -1123,10 +1041,9 @@ def _extracts_errors(triggers, summary):
for dname in dnames for dname in dnames
) )
for when, variants_by_name in pkg_cls.variants.items(): for vname, (variant, triggers) in pkg_cls.variants.items():
for vname, variant in variants_by_name.items(): summary = f"{pkg_name}: wrong 'when=' condition for the '{vname}' variant"
summary = f"{pkg_name}: wrong 'when=' condition for the '{vname}' variant" errors.extend(_extracts_errors(triggers, summary))
errors.extend(_extracts_errors([when], summary))
for when, providers, details in _error_items(pkg_cls.provided): for when, providers, details in _error_items(pkg_cls.provided):
errors.extend( errors.extend(

View File

@@ -6,6 +6,7 @@
import codecs import codecs
import collections import collections
import concurrent.futures import concurrent.futures
import contextlib
import copy import copy
import hashlib import hashlib
import io import io
@@ -24,7 +25,7 @@
import urllib.request import urllib.request
import warnings import warnings
from contextlib import closing from contextlib import closing
from typing import Dict, Iterable, List, NamedTuple, Optional, Set, Tuple, Union from typing import Dict, Generator, Iterable, List, NamedTuple, Optional, Set, Tuple, Union
import llnl.util.filesystem as fsys import llnl.util.filesystem as fsys
import llnl.util.lang import llnl.util.lang
@@ -33,6 +34,7 @@
from llnl.util.symlink import readlink from llnl.util.symlink import readlink
import spack.caches import spack.caches
import spack.cmd
import spack.config as config import spack.config as config
import spack.database as spack_db import spack.database as spack_db
import spack.error import spack.error
@@ -43,9 +45,9 @@
import spack.oci.image import spack.oci.image
import spack.oci.oci import spack.oci.oci
import spack.oci.opener import spack.oci.opener
import spack.paths
import spack.platforms import spack.platforms
import spack.relocate as relocate import spack.relocate as relocate
import spack.repo
import spack.spec import spack.spec
import spack.stage import spack.stage
import spack.store import spack.store
@@ -53,7 +55,6 @@
import spack.util.archive import spack.util.archive
import spack.util.crypto import spack.util.crypto
import spack.util.file_cache as file_cache import spack.util.file_cache as file_cache
import spack.util.filesystem as ssys
import spack.util.gpg import spack.util.gpg
import spack.util.parallel import spack.util.parallel
import spack.util.path import spack.util.path
@@ -105,7 +106,7 @@ class BuildCacheDatabase(spack_db.Database):
record_fields = ("spec", "ref_count", "in_buildcache") record_fields = ("spec", "ref_count", "in_buildcache")
def __init__(self, root): def __init__(self, root):
super().__init__(root, lock_cfg=spack_db.NO_LOCK, layout=None) super().__init__(root, lock_cfg=spack_db.NO_LOCK)
self._write_transaction_impl = llnl.util.lang.nullcontext self._write_transaction_impl = llnl.util.lang.nullcontext
self._read_transaction_impl = llnl.util.lang.nullcontext self._read_transaction_impl = llnl.util.lang.nullcontext
@@ -687,7 +688,7 @@ def get_buildfile_manifest(spec):
# Non-symlinks. # Non-symlinks.
for rel_path in visitor.files: for rel_path in visitor.files:
abs_path = os.path.join(root, rel_path) abs_path = os.path.join(root, rel_path)
m_type, m_subtype = ssys.mime_type(abs_path) m_type, m_subtype = fsys.mime_type(abs_path)
if relocate.needs_binary_relocation(m_type, m_subtype): if relocate.needs_binary_relocation(m_type, m_subtype):
# Why is this branch not part of needs_binary_relocation? :( # Why is this branch not part of needs_binary_relocation? :(
@@ -788,9 +789,7 @@ def sign_specfile(key: str, specfile_path: str) -> str:
return signed_specfile_path return signed_specfile_path
def _read_specs_and_push_index( def _read_specs_and_push_index(file_list, read_method, cache_prefix, db, temp_dir, concurrency):
file_list, read_method, cache_prefix, db: BuildCacheDatabase, temp_dir, concurrency
):
"""Read all the specs listed in the provided list, using thread given thread parallelism, """Read all the specs listed in the provided list, using thread given thread parallelism,
generate the index, and push it to the mirror. generate the index, and push it to the mirror.
@@ -814,7 +813,7 @@ def _read_specs_and_push_index(
else: else:
continue continue
db.add(fetched_spec) db.add(fetched_spec, None)
db.mark(fetched_spec, "in_buildcache", True) db.mark(fetched_spec, "in_buildcache", True)
# Now generate the index, compute its hash, and push the two files to # Now generate the index, compute its hash, and push the two files to
@@ -959,7 +958,7 @@ def _spec_files_from_cache(url: str):
raise ListMirrorSpecsError("Failed to get list of specs from {0}".format(url)) raise ListMirrorSpecsError("Failed to get list of specs from {0}".format(url))
def _url_generate_package_index(url: str, tmpdir: str, concurrency: int = 32): def generate_package_index(url: str, tmpdir: str, concurrency: int = 32):
"""Create or replace the build cache index on the given mirror. The """Create or replace the build cache index on the given mirror. The
buildcache index contains an entry for each binary package under the buildcache index contains an entry for each binary package under the
cache_prefix. cache_prefix.
@@ -1120,7 +1119,7 @@ def _exists_in_buildcache(spec: Spec, tmpdir: str, out_url: str) -> ExistsInBuil
return ExistsInBuildcache(signed, unsigned, tarball) return ExistsInBuildcache(signed, unsigned, tarball)
def _url_upload_tarball_and_specfile( def _upload_tarball_and_specfile(
spec: Spec, tmpdir: str, out_url: str, exists: ExistsInBuildcache, signing_key: Optional[str] spec: Spec, tmpdir: str, out_url: str, exists: ExistsInBuildcache, signing_key: Optional[str]
): ):
files = BuildcacheFiles(spec, tmpdir, out_url) files = BuildcacheFiles(spec, tmpdir, out_url)
@@ -1155,146 +1154,49 @@ def _url_upload_tarball_and_specfile(
) )
class Uploader:
def __init__(self, mirror: spack.mirror.Mirror, force: bool, update_index: bool):
self.mirror = mirror
self.force = force
self.update_index = update_index
self.tmpdir: str
self.executor: concurrent.futures.Executor
def __enter__(self):
self._tmpdir = tempfile.TemporaryDirectory(dir=spack.stage.get_stage_root())
self._executor = spack.util.parallel.make_concurrent_executor()
self.tmpdir = self._tmpdir.__enter__()
self.executor = self.executor = self._executor.__enter__()
return self
def __exit__(self, *args):
self._executor.__exit__(*args)
self._tmpdir.__exit__(*args)
def push_or_raise(self, specs: List[spack.spec.Spec]) -> List[spack.spec.Spec]:
skipped, errors = self.push(specs)
if errors:
raise PushToBuildCacheError(
f"Failed to push {len(errors)} specs to {self.mirror.push_url}:\n"
+ "\n".join(
f"Failed to push {_format_spec(spec)}: {error}" for spec, error in errors
)
)
return skipped
def push(
self, specs: List[spack.spec.Spec]
) -> Tuple[List[spack.spec.Spec], List[Tuple[spack.spec.Spec, BaseException]]]:
raise NotImplementedError
def tag(self, tag: str, roots: List[spack.spec.Spec]):
"""Make a list of selected specs together available under the given tag"""
pass
class OCIUploader(Uploader):
def __init__(
self,
mirror: spack.mirror.Mirror,
force: bool,
update_index: bool,
base_image: Optional[str],
) -> None:
super().__init__(mirror, force, update_index)
self.target_image = spack.oci.oci.image_from_mirror(mirror)
self.base_image = ImageReference.from_string(base_image) if base_image else None
def push(
self, specs: List[spack.spec.Spec]
) -> Tuple[List[spack.spec.Spec], List[Tuple[spack.spec.Spec, BaseException]]]:
skipped, base_images, checksums, upload_errors = _oci_push(
target_image=self.target_image,
base_image=self.base_image,
installed_specs_with_deps=specs,
force=self.force,
tmpdir=self.tmpdir,
executor=self.executor,
)
self._base_images = base_images
self._checksums = checksums
# only update index if any binaries were uploaded
if self.update_index and len(skipped) + len(upload_errors) < len(specs):
_oci_update_index(self.target_image, self.tmpdir, self.executor)
return skipped, upload_errors
def tag(self, tag: str, roots: List[spack.spec.Spec]):
tagged_image = self.target_image.with_tag(tag)
# _push_oci may not populate self._base_images if binaries were already in the registry
for spec in roots:
_oci_update_base_images(
base_image=self.base_image,
target_image=self.target_image,
spec=spec,
base_image_cache=self._base_images,
)
_oci_put_manifest(
self._base_images, self._checksums, tagged_image, self.tmpdir, None, None, *roots
)
class URLUploader(Uploader):
def __init__(
self,
mirror: spack.mirror.Mirror,
force: bool,
update_index: bool,
signing_key: Optional[str],
) -> None:
super().__init__(mirror, force, update_index)
self.url = mirror.push_url
self.signing_key = signing_key
def push(
self, specs: List[spack.spec.Spec]
) -> Tuple[List[spack.spec.Spec], List[Tuple[spack.spec.Spec, BaseException]]]:
return _url_push(
specs,
out_url=self.url,
force=self.force,
update_index=self.update_index,
signing_key=self.signing_key,
tmpdir=self.tmpdir,
executor=self.executor,
)
def make_uploader(
mirror: spack.mirror.Mirror,
force: bool = False,
update_index: bool = False,
signing_key: Optional[str] = None,
base_image: Optional[str] = None,
) -> Uploader:
"""Builder for the appropriate uploader based on the mirror type"""
if mirror.push_url.startswith("oci://"):
return OCIUploader(
mirror=mirror, force=force, update_index=update_index, base_image=base_image
)
else:
return URLUploader(
mirror=mirror, force=force, update_index=update_index, signing_key=signing_key
)
def _format_spec(spec: Spec) -> str: def _format_spec(spec: Spec) -> str:
return spec.cformat("{name}{@version}{/hash:7}") return spec.cformat("{name}{@version}{/hash:7}")
@contextlib.contextmanager
def default_push_context() -> Generator[Tuple[str, concurrent.futures.Executor], None, None]:
with tempfile.TemporaryDirectory(
dir=spack.stage.get_stage_root()
) as tmpdir, spack.util.parallel.make_concurrent_executor() as executor:
yield tmpdir, executor
def push_or_raise(
specs: List[Spec],
out_url: str,
signing_key: Optional[str],
force: bool = False,
update_index: bool = False,
) -> List[Spec]:
"""Same as push, but raises an exception on error. Returns a list of skipped specs already
present in the build cache when force=False."""
skipped, errors = push(specs, out_url, signing_key, force, update_index)
if errors:
raise PushToBuildCacheError(
f"Failed to push {len(errors)} specs to {out_url}:\n"
+ "\n".join(f"Failed to push {_format_spec(spec)}: {error}" for spec, error in errors)
)
return skipped
def push(
specs: List[Spec],
out_url: str,
signing_key: Optional[str],
force: bool = False,
update_index: bool = False,
) -> Tuple[List[Spec], List[Tuple[Spec, BaseException]]]:
"""Pushes to the provided build cache, and returns a list of skipped specs that were already
present (when force=False). Does not raise on error."""
with default_push_context() as (tmpdir, executor):
return _push(specs, out_url, signing_key, force, update_index, tmpdir, executor)
class FancyProgress: class FancyProgress:
def __init__(self, total: int): def __init__(self, total: int):
self.n = 0 self.n = 0
@@ -1332,7 +1234,7 @@ def fail(self) -> None:
tty.info(f"{self.pre}Failed to push {self.pretty_spec}") tty.info(f"{self.pre}Failed to push {self.pretty_spec}")
def _url_push( def _push(
specs: List[Spec], specs: List[Spec],
out_url: str, out_url: str,
signing_key: Optional[str], signing_key: Optional[str],
@@ -1377,7 +1279,7 @@ def _url_push(
upload_futures = [ upload_futures = [
executor.submit( executor.submit(
_url_upload_tarball_and_specfile, _upload_tarball_and_specfile,
spec, spec,
tmpdir, tmpdir,
out_url, out_url,
@@ -1407,12 +1309,12 @@ def _url_push(
if signing_key: if signing_key:
keys_tmpdir = os.path.join(tmpdir, "keys") keys_tmpdir = os.path.join(tmpdir, "keys")
os.mkdir(keys_tmpdir) os.mkdir(keys_tmpdir)
_url_push_keys(out_url, keys=[signing_key], update_index=update_index, tmpdir=keys_tmpdir) push_keys(out_url, keys=[signing_key], update_index=update_index, tmpdir=keys_tmpdir)
if update_index: if update_index:
index_tmpdir = os.path.join(tmpdir, "index") index_tmpdir = os.path.join(tmpdir, "index")
os.mkdir(index_tmpdir) os.mkdir(index_tmpdir)
_url_generate_package_index(out_url, index_tmpdir) generate_package_index(out_url, index_tmpdir)
return skipped, errors return skipped, errors
@@ -1446,9 +1348,7 @@ def _oci_push_pkg_blob(
filename = os.path.join(tmpdir, f"{spec.dag_hash()}.tar.gz") filename = os.path.join(tmpdir, f"{spec.dag_hash()}.tar.gz")
# Create an oci.image.layer aka tarball of the package # Create an oci.image.layer aka tarball of the package
compressed_tarfile_checksum, tarfile_checksum = _do_create_tarball( compressed_tarfile_checksum, tarfile_checksum = spack.oci.oci.create_tarball(spec, filename)
filename, spec.prefix, get_buildinfo_dict(spec)
)
blob = spack.oci.oci.Blob( blob = spack.oci.oci.Blob(
Digest.from_sha256(compressed_tarfile_checksum), Digest.from_sha256(compressed_tarfile_checksum),
@@ -1531,9 +1431,12 @@ def _oci_put_manifest(
for s in expected_blobs: for s in expected_blobs:
# If a layer for a dependency has gone missing (due to removed manifest in the registry, a # If a layer for a dependency has gone missing (due to removed manifest in the registry, a
# failed push, or a local forced uninstall), we cannot create a runnable container image. # failed push, or a local forced uninstall), we cannot create a runnable container image.
# If an OCI registry is only used for storage, this is not a hard error, but for now we
# raise an exception unconditionally, until someone requests a more lenient behavior.
checksum = checksums.get(s.dag_hash()) checksum = checksums.get(s.dag_hash())
if checksum: if not checksum:
config["rootfs"]["diff_ids"].append(str(checksum.uncompressed_digest)) raise MissingLayerError(f"missing layer for {_format_spec(s)}")
config["rootfs"]["diff_ids"].append(str(checksum.uncompressed_digest))
# Set the environment variables # Set the environment variables
config["config"]["Env"] = [f"{k}={v}" for k, v in env.items()] config["config"]["Env"] = [f"{k}={v}" for k, v in env.items()]
@@ -1578,7 +1481,6 @@ def _oci_put_manifest(
"size": checksums[s.dag_hash()].size, "size": checksums[s.dag_hash()].size,
} }
for s in expected_blobs for s in expected_blobs
if s.dag_hash() in checksums
), ),
], ],
} }
@@ -1617,7 +1519,7 @@ def _oci_update_base_images(
) )
def _oci_push( def _push_oci(
*, *,
target_image: ImageReference, target_image: ImageReference,
base_image: Optional[ImageReference], base_image: Optional[ImageReference],
@@ -1769,7 +1671,7 @@ def _oci_update_index(
for spec_dict in spec_dicts: for spec_dict in spec_dicts:
spec = Spec.from_dict(spec_dict) spec = Spec.from_dict(spec_dict)
db.add(spec) db.add(spec, directory_layout=None)
db.mark(spec, "in_buildcache", True) db.mark(spec, "in_buildcache", True)
# Create the index.json file # Create the index.json file
@@ -2565,8 +2467,9 @@ def install_root_node(spec, unsigned=False, force=False, sha256=None):
with spack.util.path.filter_padding(): with spack.util.path.filter_padding():
tty.msg('Installing "{0}" from a buildcache'.format(spec.format())) tty.msg('Installing "{0}" from a buildcache'.format(spec.format()))
extract_tarball(spec, download_result, force) extract_tarball(spec, download_result, force)
spec.package.windows_establish_runtime_linkage()
spack.hooks.post_install(spec, False) spack.hooks.post_install(spec, False)
spack.store.STORE.db.add(spec) spack.store.STORE.db.add(spec, spack.store.STORE.layout)
def install_single_spec(spec, unsigned=False, force=False): def install_single_spec(spec, unsigned=False, force=False):
@@ -2698,9 +2601,6 @@ def get_keys(install=False, trust=False, force=False, mirrors=None):
for mirror in mirror_collection.values(): for mirror in mirror_collection.values():
fetch_url = mirror.fetch_url fetch_url = mirror.fetch_url
# TODO: oci:// does not support signing.
if fetch_url.startswith("oci://"):
continue
keys_url = url_util.join( keys_url = url_util.join(
fetch_url, BUILD_CACHE_RELATIVE_PATH, BUILD_CACHE_KEYS_RELATIVE_PATH fetch_url, BUILD_CACHE_RELATIVE_PATH, BUILD_CACHE_KEYS_RELATIVE_PATH
) )
@@ -2745,7 +2645,7 @@ def get_keys(install=False, trust=False, force=False, mirrors=None):
) )
def _url_push_keys( def push_keys(
*mirrors: Union[spack.mirror.Mirror, str], *mirrors: Union[spack.mirror.Mirror, str],
keys: List[str], keys: List[str],
tmpdir: str, tmpdir: str,
@@ -3196,3 +3096,7 @@ class CannotListKeys(GenerateIndexError):
class PushToBuildCacheError(spack.error.SpackError): class PushToBuildCacheError(spack.error.SpackError):
"""Raised when unable to push objects to binary mirror""" """Raised when unable to push objects to binary mirror"""
class MissingLayerError(spack.error.SpackError):
"""Raised when a required layer for a dependency is missing in an OCI registry."""

View File

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

View File

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

View File

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

View File

@@ -37,16 +37,21 @@
import spack.binary_distribution import spack.binary_distribution
import spack.config import spack.config
import spack.detection import spack.detection
import spack.environment
import spack.modules
import spack.paths
import spack.platforms import spack.platforms
import spack.platforms.linux
import spack.repo
import spack.spec import spack.spec
import spack.store import spack.store
import spack.user_environment import spack.user_environment
import spack.util.environment
import spack.util.executable import spack.util.executable
import spack.util.path import spack.util.path
import spack.util.spack_yaml import spack.util.spack_yaml
import spack.util.url import spack.util.url
import spack.version import spack.version
from spack.installer import PackageInstaller
from ._common import _executables_in_store, _python_import, _root_spec, _try_import_from_store from ._common import _executables_in_store, _python_import, _root_spec, _try_import_from_store
from .clingo import ClingoBootstrapConcretizer from .clingo import ClingoBootstrapConcretizer
@@ -278,7 +283,7 @@ def try_import(self, module: str, abstract_spec_str: str) -> bool:
# Install the spec that should make the module importable # Install the spec that should make the module importable
with spack.config.override(self.mirror_scope): with spack.config.override(self.mirror_scope):
PackageInstaller([concrete_spec.package], fail_fast=True).install() concrete_spec.package.do_install(fail_fast=True)
if _try_import_from_store(module, query_spec=concrete_spec, query_info=info): if _try_import_from_store(module, query_spec=concrete_spec, query_info=info):
self.last_search = info self.last_search = info
@@ -301,7 +306,7 @@ def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bo
msg = "[BOOTSTRAP] Try installing '{0}' from sources" msg = "[BOOTSTRAP] Try installing '{0}' from sources"
tty.debug(msg.format(abstract_spec_str)) tty.debug(msg.format(abstract_spec_str))
with spack.config.override(self.mirror_scope): with spack.config.override(self.mirror_scope):
PackageInstaller([concrete_spec.package], fail_fast=True).install() concrete_spec.package.do_install()
if _executables_in_store(executables, concrete_spec, query_info=info): if _executables_in_store(executables, concrete_spec, query_info=info):
self.last_search = info self.last_search = info
return True return True
@@ -467,8 +472,7 @@ def ensure_clingo_importable_or_raise() -> None:
def gnupg_root_spec() -> str: def gnupg_root_spec() -> str:
"""Return the root spec used to bootstrap GnuPG""" """Return the root spec used to bootstrap GnuPG"""
root_spec_name = "win-gpg" if IS_WINDOWS else "gnupg" return _root_spec("gnupg@2.3:")
return _root_spec(f"{root_spec_name}@2.3:")
def ensure_gpg_in_path_or_raise() -> None: def ensure_gpg_in_path_or_raise() -> None:
@@ -478,19 +482,6 @@ def ensure_gpg_in_path_or_raise() -> None:
) )
def file_root_spec() -> str:
"""Return the root spec used to bootstrap file"""
root_spec_name = "win-file" if IS_WINDOWS else "file"
return _root_spec(root_spec_name)
def ensure_file_in_path_or_raise() -> None:
"""Ensure file is in the PATH or raise"""
return ensure_executables_in_path_or_raise(
executables=["file"], abstract_spec=file_root_spec()
)
def patchelf_root_spec() -> str: def patchelf_root_spec() -> str:
"""Return the root spec used to bootstrap patchelf""" """Return the root spec used to bootstrap patchelf"""
# 0.13.1 is the last version not to require C++17. # 0.13.1 is the last version not to require C++17.
@@ -574,15 +565,14 @@ def ensure_core_dependencies() -> None:
"""Ensure the presence of all the core dependencies.""" """Ensure the presence of all the core dependencies."""
if sys.platform.lower() == "linux": if sys.platform.lower() == "linux":
ensure_patchelf_in_path_or_raise() ensure_patchelf_in_path_or_raise()
elif sys.platform == "win32": if not IS_WINDOWS:
ensure_file_in_path_or_raise() ensure_gpg_in_path_or_raise()
ensure_gpg_in_path_or_raise()
ensure_clingo_importable_or_raise() ensure_clingo_importable_or_raise()
def all_core_root_specs() -> List[str]: def all_core_root_specs() -> List[str]:
"""Return a list of all the core root specs that may be used to bootstrap Spack""" """Return a list of all the core root specs that may be used to bootstrap Spack"""
return [clingo_root_spec(), gnupg_root_spec(), patchelf_root_spec(), file_root_spec()] return [clingo_root_spec(), gnupg_root_spec(), patchelf_root_spec()]
def bootstrapping_sources(scope: Optional[str] = None): def bootstrapping_sources(scope: Optional[str] = None):

View File

@@ -14,9 +14,9 @@
from llnl.util import tty from llnl.util import tty
import spack.environment import spack.environment
import spack.spec
import spack.tengine import spack.tengine
import spack.util.path import spack.util.cpus
import spack.util.executable
from ._common import _root_spec from ._common import _root_spec
from .config import root_path, spec_for_current_python, store_path from .config import root_path, spec_for_current_python, store_path

View File

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

View File

@@ -45,8 +45,6 @@
from itertools import chain from itertools import chain
from typing import Dict, List, Set, Tuple from typing import Dict, List, Set, Tuple
import archspec.cpu
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.string import plural from llnl.string import plural
from llnl.util.filesystem import join_path from llnl.util.filesystem import join_path
@@ -55,7 +53,6 @@
from llnl.util.tty.color import cescape, colorize from llnl.util.tty.color import cescape, colorize
from llnl.util.tty.log import MultiProcessFd from llnl.util.tty.log import MultiProcessFd
import spack.build_systems._checks
import spack.build_systems.cmake import spack.build_systems.cmake
import spack.build_systems.meson import spack.build_systems.meson
import spack.build_systems.python import spack.build_systems.python
@@ -64,20 +61,26 @@
import spack.config import spack.config
import spack.deptypes as dt import spack.deptypes as dt
import spack.error import spack.error
import spack.multimethod import spack.main
import spack.package_base import spack.package_base
import spack.paths import spack.paths
import spack.platforms import spack.platforms
import spack.repo
import spack.schema.environment import spack.schema.environment
import spack.spec import spack.spec
import spack.stage import spack.stage
import spack.store import spack.store
import spack.subprocess_context import spack.subprocess_context
import spack.user_environment
import spack.util.executable import spack.util.executable
import spack.util.path
import spack.util.pattern
from spack import traverse from spack import traverse
from spack.context import Context from spack.context import Context
from spack.error import InstallError, NoHeadersError, NoLibrariesError from spack.error import NoHeadersError, NoLibrariesError
from spack.install_test import spack_install_test_log from spack.install_test import spack_install_test_log
from spack.installer import InstallError
from spack.util.cpus import determine_number_of_jobs
from spack.util.environment import ( from spack.util.environment import (
SYSTEM_DIR_CASE_ENTRY, SYSTEM_DIR_CASE_ENTRY,
EnvironmentModifications, EnvironmentModifications,
@@ -360,7 +363,7 @@ def set_compiler_environment_variables(pkg, env):
_add_werror_handling(keep_werror, env) _add_werror_handling(keep_werror, env)
# Set the target parameters that the compiler will add # Set the target parameters that the compiler will add
isa_arg = optimization_flags(compiler, spec.target) isa_arg = spec.architecture.target.optimization_flags(compiler)
env.set("SPACK_TARGET_ARGS", isa_arg) env.set("SPACK_TARGET_ARGS", isa_arg)
# Trap spack-tracked compiler flags as appropriate. # Trap spack-tracked compiler flags as appropriate.
@@ -405,36 +408,6 @@ def set_compiler_environment_variables(pkg, env):
return env return env
def optimization_flags(compiler, target):
if spack.compilers.is_mixed_toolchain(compiler):
msg = (
"microarchitecture specific optimizations are not "
"supported yet on mixed compiler toolchains [check"
f" {compiler.name}@{compiler.version} for further details]"
)
tty.debug(msg)
return ""
# Try to check if the current compiler comes with a version number or
# has an unexpected suffix. If so, treat it as a compiler with a
# custom spec.
compiler_version = compiler.version
version_number, suffix = archspec.cpu.version_components(compiler.version)
if not version_number or suffix:
try:
compiler_version = compiler.real_version
except spack.util.executable.ProcessError as e:
# log this and just return compiler.version instead
tty.debug(str(e))
try:
result = target.optimization_flags(compiler.name, compiler_version.dotted_numeric_string)
except (ValueError, archspec.cpu.UnsupportedMicroarchitecture):
result = ""
return result
def set_wrapper_variables(pkg, env): def set_wrapper_variables(pkg, env):
"""Set environment variables used by the Spack compiler wrapper (which have the prefix """Set environment variables used by the Spack compiler wrapper (which have the prefix
`SPACK_`) and also add the compiler wrappers to PATH. `SPACK_`) and also add the compiler wrappers to PATH.
@@ -482,14 +455,11 @@ def set_wrapper_variables(pkg, env):
env.set(SPACK_DEBUG, "TRUE") env.set(SPACK_DEBUG, "TRUE")
env.set(SPACK_SHORT_SPEC, pkg.spec.short_spec) env.set(SPACK_SHORT_SPEC, pkg.spec.short_spec)
env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format("{name}-{hash:7}")) env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format("{name}-{hash:7}"))
env.set(SPACK_DEBUG_LOG_DIR, spack.paths.spack_working_dir) env.set(SPACK_DEBUG_LOG_DIR, spack.main.spack_working_dir)
# Find ccache binary and hand it to build environment
if spack.config.get("config:ccache"): if spack.config.get("config:ccache"):
# Enable ccache in the compiler wrapper
env.set(SPACK_CCACHE_BINARY, spack.util.executable.which_string("ccache", required=True)) env.set(SPACK_CCACHE_BINARY, spack.util.executable.which_string("ccache", required=True))
else:
# Avoid cache pollution if a build system forces `ccache <compiler wrapper invocation>`.
env.set("CCACHE_DISABLE", "1")
# Gather information about various types of dependencies # Gather information about various types of dependencies
link_deps = set(pkg.spec.traverse(root=False, deptype=("link"))) link_deps = set(pkg.spec.traverse(root=False, deptype=("link")))
@@ -589,7 +559,7 @@ def set_package_py_globals(pkg, context: Context = Context.BUILD):
module.std_meson_args = spack.build_systems.meson.MesonBuilder.std_args(pkg) module.std_meson_args = spack.build_systems.meson.MesonBuilder.std_args(pkg)
module.std_pip_args = spack.build_systems.python.PythonPipBuilder.std_args(pkg) module.std_pip_args = spack.build_systems.python.PythonPipBuilder.std_args(pkg)
jobs = spack.config.determine_number_of_jobs(parallel=pkg.parallel) jobs = determine_number_of_jobs(parallel=pkg.parallel)
module.make_jobs = jobs module.make_jobs = jobs
# TODO: make these build deps that can be installed if not found. # TODO: make these build deps that can be installed if not found.
@@ -815,6 +785,7 @@ def setup_package(pkg, dirty, context: Context = Context.BUILD):
# Platform specific setup goes before package specific setup. This is for setting # Platform specific setup goes before package specific setup. This is for setting
# defaults like MACOSX_DEPLOYMENT_TARGET on macOS. # defaults like MACOSX_DEPLOYMENT_TARGET on macOS.
platform = spack.platforms.by_name(pkg.spec.architecture.platform) platform = spack.platforms.by_name(pkg.spec.architecture.platform)
target = platform.target(pkg.spec.architecture.target)
platform.setup_platform_environment(pkg, env_mods) platform.setup_platform_environment(pkg, env_mods)
tty.debug("setup_package: grabbing modifications from dependencies") tty.debug("setup_package: grabbing modifications from dependencies")
@@ -839,6 +810,9 @@ def setup_package(pkg, dirty, context: Context = Context.BUILD):
for mod in pkg.compiler.modules: for mod in pkg.compiler.modules:
load_module(mod) load_module(mod)
if target and target.module_name:
load_module(target.module_name)
load_external_modules(pkg) load_external_modules(pkg)
implicit_rpaths = pkg.compiler.implicit_rpaths() implicit_rpaths = pkg.compiler.implicit_rpaths()
@@ -1162,7 +1136,7 @@ def _setup_pkg_and_run(
return_value = function(pkg, kwargs) return_value = function(pkg, kwargs)
write_pipe.send(return_value) write_pipe.send(return_value)
except spack.error.StopPhase as e: except StopPhase as e:
# Do not create a full ChildError from this, it's not an error # Do not create a full ChildError from this, it's not an error
# it's a control statement. # it's a control statement.
write_pipe.send(e) write_pipe.send(e)
@@ -1323,7 +1297,7 @@ def exitcode_msg(p):
p.join() p.join()
# If returns a StopPhase, raise it # If returns a StopPhase, raise it
if isinstance(child_result, spack.error.StopPhase): if isinstance(child_result, StopPhase):
# do not print # do not print
raise child_result raise child_result
@@ -1532,6 +1506,17 @@ def _make_child_error(msg, module, name, traceback, log, log_type, context):
return ChildError(msg, module, name, traceback, log, log_type, context) return ChildError(msg, module, name, traceback, log, log_type, context)
class StopPhase(spack.error.SpackError):
"""Pickle-able exception to control stopped builds."""
def __reduce__(self):
return _make_stop_phase, (self.message, self.long_message)
def _make_stop_phase(msg, long_msg):
return StopPhase(msg, long_msg)
def write_log_summary(out, log_type, log, last=None): def write_log_summary(out, log_type, log, last=None):
errors, warnings = parse_log_events(log) errors, warnings = parse_log_events(log)
nerr = len(errors) nerr = len(errors)
@@ -1565,21 +1550,21 @@ class ModuleChangePropagator:
_PROTECTED_NAMES = ("package", "current_module", "modules_in_mro", "_set_attributes") _PROTECTED_NAMES = ("package", "current_module", "modules_in_mro", "_set_attributes")
def __init__(self, package: spack.package_base.PackageBase) -> None: def __init__(self, package):
self._set_self_attributes("package", package) self._set_self_attributes("package", package)
self._set_self_attributes("current_module", package.module) self._set_self_attributes("current_module", package.module)
#: Modules for the classes in the MRO up to PackageBase #: Modules for the classes in the MRO up to PackageBase
modules_in_mro = [] modules_in_mro = []
for cls in package.__class__.__mro__: for cls in inspect.getmro(type(package)):
module = getattr(cls, "module", None) module = cls.module
if module is None or module is spack.package_base: if module == self.current_module:
break
if module is self.current_module:
continue continue
if module == spack.package_base:
break
modules_in_mro.append(module) modules_in_mro.append(module)
self._set_self_attributes("modules_in_mro", modules_in_mro) self._set_self_attributes("modules_in_mro", modules_in_mro)
self._set_self_attributes("_set_attributes", {}) self._set_self_attributes("_set_attributes", {})

View File

@@ -8,7 +8,7 @@
import llnl.util.lang import llnl.util.lang
import spack.builder import spack.builder
import spack.error import spack.installer
import spack.relocate import spack.relocate
import spack.spec import spack.spec
import spack.store import spack.store
@@ -34,7 +34,7 @@ def check_paths(path_list, filetype, predicate):
if not predicate(abs_path): if not predicate(abs_path):
msg = "Install failed for {0}. No such {1} in prefix: {2}" msg = "Install failed for {0}. No such {1} in prefix: {2}"
msg = msg.format(pkg.name, filetype, path) msg = msg.format(pkg.name, filetype, path)
raise spack.error.InstallError(msg) raise spack.installer.InstallError(msg)
check_paths(pkg.sanity_check_is_file, "file", os.path.isfile) check_paths(pkg.sanity_check_is_file, "file", os.path.isfile)
check_paths(pkg.sanity_check_is_dir, "directory", os.path.isdir) check_paths(pkg.sanity_check_is_dir, "directory", os.path.isdir)
@@ -42,7 +42,7 @@ def check_paths(path_list, filetype, predicate):
ignore_file = llnl.util.lang.match_predicate(spack.store.STORE.layout.hidden_file_regexes) ignore_file = llnl.util.lang.match_predicate(spack.store.STORE.layout.hidden_file_regexes)
if all(map(ignore_file, os.listdir(pkg.prefix))): if all(map(ignore_file, os.listdir(pkg.prefix))):
msg = "Install failed for {0}. Nothing was installed!" msg = "Install failed for {0}. Nothing was installed!"
raise spack.error.InstallError(msg.format(pkg.name)) raise spack.installer.InstallError(msg.format(pkg.name))
def apply_macos_rpath_fixups(builder: spack.builder.Builder): def apply_macos_rpath_fixups(builder: spack.builder.Builder):

View File

@@ -2,8 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import llnl.util.filesystem as fs import llnl.util.filesystem as fs
import spack.directives import spack.directives
@@ -48,12 +46,18 @@ class AspellDictPackage(AutotoolsPackage):
#: Override the default autotools builder #: Override the default autotools builder
AutotoolsBuilder = AspellBuilder AutotoolsBuilder = AspellBuilder
def patch(self): def view_destination(self, view):
aspell_spec = self.spec["aspell"] aspell_spec = self.spec["aspell"]
if view.get_projection_for_spec(aspell_spec) != aspell_spec.prefix:
raise spack.package_base.ExtensionError(
"aspell does not support non-global extensions"
)
aspell = aspell_spec.command aspell = aspell_spec.command
dictdir = aspell("dump", "config", "dict-dir", output=str).strip() return aspell("dump", "config", "dict-dir", output=str).strip()
datadir = aspell("dump", "config", "data-dir", output=str).strip()
dictdir = os.path.relpath(dictdir, aspell_spec.prefix) def view_source(self):
datadir = os.path.relpath(datadir, aspell_spec.prefix) return self.prefix.lib
fs.filter_file(r"^dictdir=.*$", f"dictdir=/{dictdir}", "configure")
fs.filter_file(r"^datadir=.*$", f"datadir=/{datadir}", "configure") def patch(self):
fs.filter_file(r"^dictdir=.*$", "dictdir=/lib", "configure")
fs.filter_file(r"^datadir=.*$", "datadir=/lib", "configure")

View File

@@ -2,6 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
import os import os
import os.path import os.path
import stat import stat
@@ -13,7 +14,6 @@
import spack.build_environment import spack.build_environment
import spack.builder import spack.builder
import spack.error
import spack.package_base import spack.package_base
from spack.directives import build_system, conflicts, depends_on from spack.directives import build_system, conflicts, depends_on
from spack.multimethod import when from spack.multimethod import when
@@ -249,7 +249,7 @@ def runs_ok(script_abs_path):
# An external gnuconfig may not not have a prefix. # An external gnuconfig may not not have a prefix.
if gnuconfig_dir is None: if gnuconfig_dir is None:
raise spack.error.InstallError( raise spack.build_environment.InstallError(
"Spack could not find substitutes for GNU config files because no " "Spack could not find substitutes for GNU config files because no "
"prefix is available for the `gnuconfig` package. Make sure you set a " "prefix is available for the `gnuconfig` package. Make sure you set a "
"prefix path instead of modules for external `gnuconfig`." "prefix path instead of modules for external `gnuconfig`."
@@ -269,7 +269,7 @@ def runs_ok(script_abs_path):
msg += ( msg += (
" or the `gnuconfig` package prefix is misconfigured as" " an external package" " or the `gnuconfig` package prefix is misconfigured as" " an external package"
) )
raise spack.error.InstallError(msg) raise spack.build_environment.InstallError(msg)
# Filter working substitutes # Filter working substitutes
candidates = [f for f in candidates if runs_ok(f)] candidates = [f for f in candidates if runs_ok(f)]
@@ -294,7 +294,9 @@ def runs_ok(script_abs_path):
and set the prefix to the directory containing the `config.guess` and and set the prefix to the directory containing the `config.guess` and
`config.sub` files. `config.sub` files.
""" """
raise spack.error.InstallError(msg.format(", ".join(to_be_found), self.name)) raise spack.build_environment.InstallError(
msg.format(", ".join(to_be_found), self.name)
)
# Copy the good files over the bad ones # Copy the good files over the bad ones
for abs_path in to_be_patched: for abs_path in to_be_patched:
@@ -547,12 +549,13 @@ def autoreconf(self, pkg, spec, prefix):
tty.warn("* a custom AUTORECONF phase in the package *") tty.warn("* a custom AUTORECONF phase in the package *")
tty.warn("*********************************************************") tty.warn("*********************************************************")
with fs.working_dir(self.configure_directory): with fs.working_dir(self.configure_directory):
m = inspect.getmodule(self.pkg)
# This line is what is needed most of the time # This line is what is needed most of the time
# --install, --verbose, --force # --install, --verbose, --force
autoreconf_args = ["-ivf"] autoreconf_args = ["-ivf"]
autoreconf_args += self.autoreconf_search_path_args autoreconf_args += self.autoreconf_search_path_args
autoreconf_args += self.autoreconf_extra_args autoreconf_args += self.autoreconf_extra_args
self.pkg.module.autoreconf(*autoreconf_args) m.autoreconf(*autoreconf_args)
@property @property
def autoreconf_search_path_args(self): def autoreconf_search_path_args(self):
@@ -576,9 +579,7 @@ def set_configure_or_die(self):
raise RuntimeError(msg.format(self.configure_directory)) raise RuntimeError(msg.format(self.configure_directory))
# Monkey-patch the configure script in the corresponding module # Monkey-patch the configure script in the corresponding module
globals_for_pkg = spack.build_environment.ModuleChangePropagator(self.pkg) inspect.getmodule(self.pkg).configure = Executable(self.configure_abs_path)
globals_for_pkg.configure = Executable(self.configure_abs_path)
globals_for_pkg.propagate_changes_to_mro()
def configure_args(self): def configure_args(self):
"""Return the list of all the arguments that must be passed to configure, """Return the list of all the arguments that must be passed to configure,
@@ -595,7 +596,7 @@ def configure(self, pkg, spec, prefix):
options += self.configure_args() options += self.configure_args()
with fs.working_dir(self.build_directory, create=True): with fs.working_dir(self.build_directory, create=True):
pkg.module.configure(*options) inspect.getmodule(self.pkg).configure(*options)
def build(self, pkg, spec, prefix): def build(self, pkg, spec, prefix):
"""Run "make" on the build targets specified by the builder.""" """Run "make" on the build targets specified by the builder."""
@@ -603,12 +604,12 @@ def build(self, pkg, spec, prefix):
params = ["V=1"] params = ["V=1"]
params += self.build_targets params += self.build_targets
with fs.working_dir(self.build_directory): with fs.working_dir(self.build_directory):
pkg.module.make(*params) inspect.getmodule(self.pkg).make(*params)
def install(self, pkg, spec, prefix): def install(self, pkg, spec, prefix):
"""Run "make" on the install targets specified by the builder.""" """Run "make" on the install targets specified by the builder."""
with fs.working_dir(self.build_directory): with fs.working_dir(self.build_directory):
pkg.module.make(*self.install_targets) inspect.getmodule(self.pkg).make(*self.install_targets)
spack.builder.run_after("build")(execute_build_time_tests) spack.builder.run_after("build")(execute_build_time_tests)
@@ -687,8 +688,9 @@ def _activate_or_not(
variant = variant or name variant = variant or name
# Defensively look that the name passed as argument is among variants # Defensively look that the name passed as argument is among
if not self.pkg.has_variant(variant): # variants
if variant not in self.pkg.variants:
msg = '"{0}" is not a variant of "{1}"' msg = '"{0}" is not a variant of "{1}"'
raise KeyError(msg.format(variant, self.pkg.name)) raise KeyError(msg.format(variant, self.pkg.name))
@@ -697,19 +699,27 @@ def _activate_or_not(
# Create a list of pairs. Each pair includes a configuration # Create a list of pairs. Each pair includes a configuration
# option and whether or not that option is activated # option and whether or not that option is activated
vdef = self.pkg.get_variant(variant) variant_desc, _ = self.pkg.variants[variant]
if set(vdef.values) == set((True, False)): if set(variant_desc.values) == set((True, False)):
# BoolValuedVariant carry information about a single option. # BoolValuedVariant carry information about a single option.
# Nonetheless, for uniformity of treatment we'll package them # Nonetheless, for uniformity of treatment we'll package them
# in an iterable of one element. # in an iterable of one element.
options = [(name, f"+{variant}" in spec)] condition = "+{name}".format(name=variant)
options = [(name, condition in spec)]
else: else:
condition = "{variant}={value}"
# "feature_values" is used to track values which correspond to # "feature_values" is used to track values which correspond to
# features which can be enabled or disabled as understood by the # features which can be enabled or disabled as understood by the
# package's build system. It excludes values which have special # package's build system. It excludes values which have special
# meanings and do not correspond to features (e.g. "none") # meanings and do not correspond to features (e.g. "none")
feature_values = getattr(vdef.values, "feature_values", None) or vdef.values feature_values = (
options = [(value, f"{variant}={value}" in spec) for value in feature_values] getattr(variant_desc.values, "feature_values", None) or variant_desc.values
)
options = [
(value, condition.format(variant=variant, value=value) in spec)
for value in feature_values
]
# For each allowed value in the list of values # For each allowed value in the list of values
for option_value, activated in options: for option_value, activated in options:

View File

@@ -89,7 +89,7 @@ def define_cmake_cache_from_variant(self, cmake_var, variant=None, comment=""):
if variant is None: if variant is None:
variant = cmake_var.lower() variant = cmake_var.lower()
if not self.pkg.has_variant(variant): if variant not in self.pkg.variants:
raise KeyError('"{0}" is not a variant of "{1}"'.format(variant, self.pkg.name)) raise KeyError('"{0}" is not a variant of "{1}"'.format(variant, self.pkg.name))
if variant not in self.pkg.spec.variants: if variant not in self.pkg.spec.variants:

View File

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

View File

@@ -3,6 +3,7 @@
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import collections.abc import collections.abc
import inspect
import os import os
import pathlib import pathlib
import platform import platform
@@ -15,7 +16,6 @@
import spack.build_environment import spack.build_environment
import spack.builder import spack.builder
import spack.deptypes as dt import spack.deptypes as dt
import spack.error
import spack.package_base import spack.package_base
from spack.directives import build_system, conflicts, depends_on, variant from spack.directives import build_system, conflicts, depends_on, variant
from spack.multimethod import when from spack.multimethod import when
@@ -108,11 +108,6 @@ def _conditional_cmake_defaults(pkg: spack.package_base.PackageBase, args: List[
if _supports_compilation_databases(pkg): if _supports_compilation_databases(pkg):
args.append(CMakeBuilder.define("CMAKE_EXPORT_COMPILE_COMMANDS", True)) args.append(CMakeBuilder.define("CMAKE_EXPORT_COMPILE_COMMANDS", True))
# Enable MACOSX_RPATH by default when cmake_minimum_required < 3
# https://cmake.org/cmake/help/latest/policy/CMP0042.html
if pkg.spec.satisfies("platform=darwin") and cmake.satisfies("@3:"):
args.append(CMakeBuilder.define("CMAKE_POLICY_DEFAULT_CMP0042", "NEW"))
def generator(*names: str, default: Optional[str] = None): def generator(*names: str, default: Optional[str] = None):
"""The build system generator to use. """The build system generator to use.
@@ -146,7 +141,6 @@ def _values(x):
default=default, default=default,
values=_values, values=_values,
description="the build system generator to use", description="the build system generator to use",
when="build_system=cmake",
) )
for x in not_used: for x in not_used:
conflicts(f"generator={x}") conflicts(f"generator={x}")
@@ -346,7 +340,7 @@ def std_args(pkg, generator=None):
msg = "Invalid CMake generator: '{0}'\n".format(generator) msg = "Invalid CMake generator: '{0}'\n".format(generator)
msg += "CMakePackage currently supports the following " msg += "CMakePackage currently supports the following "
msg += "primary generators: '{0}'".format("', '".join(valid_primary_generators)) msg += "primary generators: '{0}'".format("', '".join(valid_primary_generators))
raise spack.error.InstallError(msg) raise spack.package_base.InstallError(msg)
try: try:
build_type = pkg.spec.variants["build_type"].value build_type = pkg.spec.variants["build_type"].value
@@ -506,7 +500,7 @@ def define_from_variant(self, cmake_var, variant=None):
if variant is None: if variant is None:
variant = cmake_var.lower() variant = cmake_var.lower()
if not self.pkg.has_variant(variant): if variant not in self.pkg.variants:
raise KeyError('"{0}" is not a variant of "{1}"'.format(variant, self.pkg.name)) raise KeyError('"{0}" is not a variant of "{1}"'.format(variant, self.pkg.name))
if variant not in self.pkg.spec.variants: if variant not in self.pkg.spec.variants:
@@ -545,24 +539,24 @@ def cmake(self, pkg, spec, prefix):
options += self.cmake_args() options += self.cmake_args()
options.append(os.path.abspath(self.root_cmakelists_dir)) options.append(os.path.abspath(self.root_cmakelists_dir))
with fs.working_dir(self.build_directory, create=True): with fs.working_dir(self.build_directory, create=True):
pkg.module.cmake(*options) inspect.getmodule(self.pkg).cmake(*options)
def build(self, pkg, spec, prefix): def build(self, pkg, spec, prefix):
"""Make the build targets""" """Make the build targets"""
with fs.working_dir(self.build_directory): with fs.working_dir(self.build_directory):
if self.generator == "Unix Makefiles": if self.generator == "Unix Makefiles":
pkg.module.make(*self.build_targets) inspect.getmodule(self.pkg).make(*self.build_targets)
elif self.generator == "Ninja": elif self.generator == "Ninja":
self.build_targets.append("-v") self.build_targets.append("-v")
pkg.module.ninja(*self.build_targets) inspect.getmodule(self.pkg).ninja(*self.build_targets)
def install(self, pkg, spec, prefix): def install(self, pkg, spec, prefix):
"""Make the install targets""" """Make the install targets"""
with fs.working_dir(self.build_directory): with fs.working_dir(self.build_directory):
if self.generator == "Unix Makefiles": if self.generator == "Unix Makefiles":
pkg.module.make(*self.install_targets) inspect.getmodule(self.pkg).make(*self.install_targets)
elif self.generator == "Ninja": elif self.generator == "Ninja":
pkg.module.ninja(*self.install_targets) inspect.getmodule(self.pkg).ninja(*self.install_targets)
spack.builder.run_after("build")(execute_build_time_tests) spack.builder.run_after("build")(execute_build_time_tests)

View File

@@ -14,7 +14,6 @@
import spack.compiler import spack.compiler
import spack.package_base import spack.package_base
import spack.util.executable
# Local "type" for type hints # Local "type" for type hints
Path = Union[str, pathlib.Path] Path = Union[str, pathlib.Path]

View File

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

View File

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

View File

@@ -22,10 +22,9 @@
install, install,
) )
import spack.builder
import spack.error import spack.error
from spack.build_environment import dso_suffix from spack.build_environment import dso_suffix
from spack.error import InstallError from spack.package_base import InstallError
from spack.util.environment import EnvironmentModifications from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable from spack.util.executable import Executable
from spack.util.prefix import Prefix from spack.util.prefix import Prefix

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@
import spack.util.path import spack.util.path
from spack.build_environment import dso_suffix from spack.build_environment import dso_suffix
from spack.directives import conflicts, license, redistribute, variant from spack.directives import conflicts, license, redistribute, variant
from spack.error import InstallError from spack.package_base import InstallError
from spack.util.environment import EnvironmentModifications from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable from spack.util.executable import Executable

View File

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

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import functools import functools
import inspect
import operator import operator
import os import os
import re import re
@@ -16,7 +17,7 @@
import llnl.util.filesystem as fs import llnl.util.filesystem as fs
import llnl.util.lang as lang import llnl.util.lang as lang
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.filesystem import HeaderList, LibraryList, join_path from llnl.util.filesystem import HeaderList, LibraryList
import spack.builder import spack.builder
import spack.config import spack.config
@@ -24,8 +25,6 @@
import spack.detection import spack.detection
import spack.multimethod import spack.multimethod
import spack.package_base import spack.package_base
import spack.platforms
import spack.repo
import spack.spec import spack.spec
import spack.store import spack.store
from spack.directives import build_system, depends_on, extends from spack.directives import build_system, depends_on, extends
@@ -121,12 +120,6 @@ def skip_modules(self) -> Iterable[str]:
""" """
return [] return []
@property
def bindir(self) -> str:
"""Path to Python package's bindir, bin on unix like OS's Scripts on Windows"""
windows = self.spec.satisfies("platform=windows")
return join_path(self.spec.prefix, "Scripts" if windows else "bin")
def view_file_conflicts(self, view, merge_map): def view_file_conflicts(self, view, merge_map):
"""Report all file conflicts, excepting special cases for python. """Report all file conflicts, excepting special cases for python.
Specifically, this does not report errors for duplicate Specifically, this does not report errors for duplicate
@@ -229,7 +222,7 @@ def test_imports(self) -> None:
# Make sure we are importing the installed modules, # Make sure we are importing the installed modules,
# not the ones in the source directory # not the ones in the source directory
python = self.module.python python = inspect.getmodule(self).python # type: ignore[union-attr]
for module in self.import_modules: for module in self.import_modules:
with test_part( with test_part(
self, self,
@@ -316,9 +309,9 @@ def get_external_python_for_prefix(self):
) )
python_externals_detected = [ python_externals_detected = [
spec d.spec
for spec in python_externals_detection.get("python", []) for d in python_externals_detection.get("python", [])
if spec.external_path == self.spec.external_path if d.prefix == self.spec.external_path
] ]
if python_externals_detected: if python_externals_detected:
return python_externals_detected[0] return python_externals_detected[0]

View File

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

View File

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

View File

@@ -11,9 +11,9 @@
import spack.builder import spack.builder
from spack.build_environment import SPACK_NO_PARALLEL_MAKE from spack.build_environment import SPACK_NO_PARALLEL_MAKE
from spack.config import determine_number_of_jobs
from spack.directives import build_system, extends, maintainers from spack.directives import build_system, extends, maintainers
from spack.package_base import PackageBase from spack.package_base import PackageBase
from spack.util.cpus import determine_number_of_jobs
from spack.util.environment import env_flag from spack.util.environment import env_flag
from spack.util.executable import Executable, ProcessError from spack.util.executable import Executable, ProcessError

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,12 +6,12 @@
import collections.abc import collections.abc
import copy import copy
import functools import functools
import inspect
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from llnl.util import lang from llnl.util import lang
import spack.error import spack.build_environment
import spack.multimethod
#: Builder classes, as registered by the "builder" decorator #: Builder classes, as registered by the "builder" decorator
BUILDER_CLS = {} BUILDER_CLS = {}
@@ -96,10 +96,11 @@ class hierarchy (look at AspellDictPackage for an example of that)
Args: Args:
pkg (spack.package_base.PackageBase): package object for which we need a builder pkg (spack.package_base.PackageBase): package object for which we need a builder
""" """
package_module = inspect.getmodule(pkg)
package_buildsystem = buildsystem_name(pkg) package_buildsystem = buildsystem_name(pkg)
default_builder_cls = BUILDER_CLS[package_buildsystem] default_builder_cls = BUILDER_CLS[package_buildsystem]
builder_cls_name = default_builder_cls.__name__ builder_cls_name = default_builder_cls.__name__
builder_cls = getattr(pkg.module, builder_cls_name, None) builder_cls = getattr(package_module, builder_cls_name, None)
if builder_cls: if builder_cls:
return builder_cls(pkg) return builder_cls(pkg)
@@ -294,11 +295,7 @@ def _decorator(fn):
return _decorator return _decorator
class BuilderMeta( class BuilderMeta(PhaseCallbacksMeta, type(collections.abc.Sequence)): # type: ignore
PhaseCallbacksMeta,
spack.multimethod.MultiMethodMeta,
type(collections.abc.Sequence), # type: ignore
):
pass pass
@@ -461,13 +458,15 @@ def _on_phase_start(self, instance):
# If a phase has a matching stop_before_phase attribute, # If a phase has a matching stop_before_phase attribute,
# stop the installation process raising a StopPhase # stop the installation process raising a StopPhase
if getattr(instance, "stop_before_phase", None) == self.name: if getattr(instance, "stop_before_phase", None) == self.name:
raise spack.error.StopPhase("Stopping before '{0}' phase".format(self.name)) raise spack.build_environment.StopPhase(
"Stopping before '{0}' phase".format(self.name)
)
def _on_phase_exit(self, instance): def _on_phase_exit(self, instance):
# If a phase has a matching last_phase attribute, # If a phase has a matching last_phase attribute,
# stop the installation process raising a StopPhase # stop the installation process raising a StopPhase
if getattr(instance, "last_phase", None) == self.name: if getattr(instance, "last_phase", None) == self.name:
raise spack.error.StopPhase("Stopping at '{0}' phase".format(self.name)) raise spack.build_environment.StopPhase("Stopping at '{0}' phase".format(self.name))
def copy(self): def copy(self):
return copy.deepcopy(self) return copy.deepcopy(self)

View File

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

View File

@@ -31,7 +31,6 @@
import spack import spack
import spack.binary_distribution as bindist import spack.binary_distribution as bindist
import spack.concretize
import spack.config as cfg import spack.config as cfg
import spack.environment as ev import spack.environment as ev
import spack.main import spack.main
@@ -39,6 +38,7 @@
import spack.paths import spack.paths
import spack.repo import spack.repo
import spack.spec import spack.spec
import spack.stage
import spack.util.git import spack.util.git
import spack.util.gpg as gpg_util import spack.util.gpg as gpg_util
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
@@ -1110,8 +1110,7 @@ def main_script_replacements(cmd):
cdash_handler.populate_buildgroup(all_job_names) cdash_handler.populate_buildgroup(all_job_names)
except (SpackError, HTTPError, URLError, TimeoutError) as err: except (SpackError, HTTPError, URLError, TimeoutError) as err:
tty.warn(f"Problem populating buildgroup: {err}") tty.warn(f"Problem populating buildgroup: {err}")
elif cdash_config: else:
# warn only if there was actually a CDash configuration.
tty.warn("Unable to populate buildgroup without CDash credentials") tty.warn("Unable to populate buildgroup without CDash credentials")
service_job_retries = { service_job_retries = {
@@ -1219,8 +1218,8 @@ def main_script_replacements(cmd):
# Capture the version of Spack used to generate the pipeline, that can be # Capture the version of Spack used to generate the pipeline, that can be
# passed to `git checkout` for version consistency. If we aren't in a Git # passed to `git checkout` for version consistency. If we aren't in a Git
# repository, presume we are a Spack release and use the Git tag instead. # repository, presume we are a Spack release and use the Git tag instead.
spack_version = spack.get_version() spack_version = spack.main.get_version()
version_to_clone = spack.get_spack_commit() or f"v{spack.spack_version}" version_to_clone = spack.main.get_spack_commit() or f"v{spack.spack_version}"
output_object["variables"] = { output_object["variables"] = {
"SPACK_ARTIFACTS_ROOT": rel_artifacts_root, "SPACK_ARTIFACTS_ROOT": rel_artifacts_root,
@@ -1383,10 +1382,8 @@ def push_to_build_cache(spec: spack.spec.Spec, mirror_url: str, sign_binaries: b
""" """
tty.debug(f"Pushing to build cache ({'signed' if sign_binaries else 'unsigned'})") tty.debug(f"Pushing to build cache ({'signed' if sign_binaries else 'unsigned'})")
signing_key = bindist.select_signing_key() if sign_binaries else None signing_key = bindist.select_signing_key() if sign_binaries else None
mirror = spack.mirror.Mirror.from_url(mirror_url)
try: try:
with bindist.make_uploader(mirror, signing_key=signing_key) as uploader: bindist.push_or_raise([spec], out_url=mirror_url, signing_key=signing_key)
uploader.push_or_raise([spec])
return True return True
except bindist.PushToBuildCacheError as e: except bindist.PushToBuildCacheError as e:
tty.error(f"Problem writing to {mirror_url}: {e}") tty.error(f"Problem writing to {mirror_url}: {e}")
@@ -1436,6 +1433,10 @@ def copy_stage_logs_to_artifacts(job_spec: spack.spec.Spec, job_log_dir: str) ->
job_log_dir: path into which build log should be copied job_log_dir: path into which build log should be copied
""" """
tty.debug(f"job spec: {job_spec}") tty.debug(f"job spec: {job_spec}")
if not job_spec:
msg = f"Cannot copy stage logs: job spec ({job_spec}) is required"
tty.error(msg)
return
try: try:
pkg_cls = spack.repo.PATH.get_pkg_class(job_spec.name) pkg_cls = spack.repo.PATH.get_pkg_class(job_spec.name)

View File

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

View File

@@ -11,7 +11,6 @@
import llnl.util.tty.color as color import llnl.util.tty.color as color
import spack.platforms import spack.platforms
import spack.spec
description = "print architecture information about this machine" description = "print architecture information about this machine"
section = "system" section = "system"

View File

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

View File

@@ -16,11 +16,11 @@
import spack.bootstrap.config import spack.bootstrap.config
import spack.bootstrap.core import spack.bootstrap.core
import spack.config import spack.config
import spack.main
import spack.mirror import spack.mirror
import spack.spec import spack.spec
import spack.stage import spack.stage
import spack.util.path import spack.util.path
import spack.util.spack_yaml
from spack.cmd.common import arguments from spack.cmd.common import arguments
description = "manage bootstrap configuration" description = "manage bootstrap configuration"

View File

@@ -23,15 +23,21 @@
import spack.error import spack.error
import spack.mirror import spack.mirror
import spack.oci.oci import spack.oci.oci
import spack.oci.opener
import spack.relocate
import spack.repo
import spack.spec import spack.spec
import spack.stage import spack.stage
import spack.store import spack.store
import spack.user_environment
import spack.util.crypto
import spack.util.parallel import spack.util.parallel
import spack.util.url as url_util import spack.util.url as url_util
import spack.util.web as web_util import spack.util.web as web_util
from spack import traverse from spack import traverse
from spack.cmd import display_specs from spack.cmd import display_specs
from spack.cmd.common import arguments from spack.cmd.common import arguments
from spack.oci.image import ImageReference
from spack.spec import Spec, save_dependency_specfiles from spack.spec import Spec, save_dependency_specfiles
description = "create, download and install binary packages" description = "create, download and install binary packages"
@@ -386,8 +392,13 @@ def push_fn(args):
else: else:
roots = spack.cmd.require_active_env(cmd_name="buildcache push").concrete_roots() roots = spack.cmd.require_active_env(cmd_name="buildcache push").concrete_roots()
mirror = args.mirror mirror: spack.mirror.Mirror = args.mirror
assert isinstance(mirror, spack.mirror.Mirror)
# Check if this is an OCI image.
try:
target_image = spack.oci.oci.image_from_mirror(mirror)
except ValueError:
target_image = None
push_url = mirror.push_url push_url = mirror.push_url
@@ -398,11 +409,14 @@ def push_fn(args):
unsigned = not (args.key or args.signed) unsigned = not (args.key or args.signed)
# For OCI images, we require dependencies to be pushed for now. # For OCI images, we require dependencies to be pushed for now.
if mirror.push_url.startswith("oci://") and not unsigned: if target_image:
tty.warn( if "dependencies" not in args.things_to_install:
"Code signing is currently not supported for OCI images. " tty.die("Dependencies must be pushed for OCI images.")
"Use --unsigned to silence this warning." if not unsigned:
) tty.warn(
"Code signing is currently not supported for OCI images. "
"Use --unsigned to silence this warning."
)
unsigned = True unsigned = True
# Select a signing key, or None if unsigned. # Select a signing key, or None if unsigned.
@@ -433,17 +447,49 @@ def push_fn(args):
(s, PackageNotInstalledError("package not installed")) for s in not_installed (s, PackageNotInstalledError("package not installed")) for s in not_installed
) )
with bindist.make_uploader( with bindist.default_push_context() as (tmpdir, executor):
mirror=mirror, if target_image:
force=args.force, base_image = ImageReference.from_string(args.base_image) if args.base_image else None
update_index=args.update_index, skipped, base_images, checksums, upload_errors = bindist._push_oci(
signing_key=signing_key, target_image=target_image,
base_image=args.base_image, base_image=base_image,
) as uploader: installed_specs_with_deps=specs,
skipped, upload_errors = uploader.push(specs=specs) force=args.force,
failed.extend(upload_errors) tmpdir=tmpdir,
if not upload_errors and args.tag: executor=executor,
uploader.tag(args.tag, roots) )
if upload_errors:
failed.extend(upload_errors)
# Apart from creating manifests for each individual spec, we allow users to create a
# separate image tag for all root specs and their runtime dependencies.
elif args.tag:
tagged_image = target_image.with_tag(args.tag)
# _push_oci may not populate base_images if binaries were already in the registry
for spec in roots:
bindist._oci_update_base_images(
base_image=base_image,
target_image=target_image,
spec=spec,
base_image_cache=base_images,
)
bindist._oci_put_manifest(
base_images, checksums, tagged_image, tmpdir, None, None, *roots
)
tty.info(f"Tagged {tagged_image}")
else:
skipped, upload_errors = bindist._push(
specs,
out_url=push_url,
force=args.force,
update_index=args.update_index,
signing_key=signing_key,
tmpdir=tmpdir,
executor=executor,
)
failed.extend(upload_errors)
if skipped: if skipped:
if len(specs) == 1: if len(specs) == 1:
@@ -455,7 +501,7 @@ def push_fn(args):
"The following {} specs were skipped as they already exist in the buildcache:\n" "The following {} specs were skipped as they already exist in the buildcache:\n"
" {}\n" " {}\n"
" Use --force to overwrite them.".format( " Use --force to overwrite them.".format(
len(skipped), ", ".join(elide_list([_format_spec(s) for s in skipped], 5)) len(skipped), ", ".join(elide_list(skipped, 5))
) )
) )
@@ -476,6 +522,13 @@ def push_fn(args):
), ),
) )
# Update the OCI index if requested
if target_image and len(skipped) < len(specs) and args.update_index:
with tempfile.TemporaryDirectory(
dir=spack.stage.get_stage_root()
) as tmpdir, spack.util.parallel.make_concurrent_executor() as executor:
bindist._oci_update_index(target_image, tmpdir, executor)
def install_fn(args): def install_fn(args):
"""install from a binary package""" """install from a binary package"""
@@ -763,7 +816,7 @@ def update_index(mirror: spack.mirror.Mirror, update_keys=False):
url = mirror.push_url url = mirror.push_url
with tempfile.TemporaryDirectory(dir=spack.stage.get_stage_root()) as tmpdir: with tempfile.TemporaryDirectory(dir=spack.stage.get_stage_root()) as tmpdir:
bindist._url_generate_package_index(url, tmpdir) bindist.generate_package_index(url, tmpdir)
if update_keys: if update_keys:
keys_url = url_util.join( keys_url = url_util.join(

View File

@@ -4,7 +4,6 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import spack.cmd import spack.cmd
import spack.spec
from spack.cmd.common import arguments from spack.cmd.common import arguments
description = "change an existing spec in an environment" description = "change an existing spec in an environment"

View File

@@ -15,6 +15,7 @@
import spack.repo import spack.repo
import spack.spec import spack.spec
import spack.stage import spack.stage
import spack.util.crypto
import spack.util.web as web_util import spack.util.web as web_util
from spack.cmd.common import arguments from spack.cmd.common import arguments
from spack.package_base import ( from spack.package_base import (

View File

@@ -19,6 +19,7 @@
import spack.cmd.buildcache as buildcache import spack.cmd.buildcache as buildcache
import spack.config as cfg import spack.config as cfg
import spack.environment as ev import spack.environment as ev
import spack.environment.depfile
import spack.hash_types as ht import spack.hash_types as ht
import spack.mirror import spack.mirror
import spack.util.gpg as gpg_util import spack.util.gpg as gpg_util

View File

@@ -10,9 +10,11 @@
import llnl.util.filesystem import llnl.util.filesystem
import llnl.util.tty as tty import llnl.util.tty as tty
import spack.bootstrap
import spack.caches import spack.caches
import spack.cmd import spack.cmd.test
import spack.config import spack.config
import spack.repo
import spack.stage import spack.stage
import spack.store import spack.store
import spack.util.path import spack.util.path

View File

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

View File

@@ -15,6 +15,7 @@
import spack.deptypes as dt import spack.deptypes as dt
import spack.environment as ev import spack.environment as ev
import spack.mirror import spack.mirror
import spack.modules
import spack.reporters import spack.reporters
import spack.spec import spack.spec
import spack.store import spack.store

View File

@@ -9,7 +9,6 @@
import llnl.util.tty as tty import llnl.util.tty as tty
import spack.cmd import spack.cmd
import spack.spec
display_args = {"long": True, "show_flags": False, "variants": False, "indent": 4} display_args = {"long": True, "show_flags": False, "variants": False, "indent": 4}

View File

@@ -10,6 +10,7 @@
import spack.cmd import spack.cmd
import spack.deptypes as dt import spack.deptypes as dt
import spack.error import spack.error
import spack.paths
import spack.spec import spack.spec
import spack.store import spack.store
from spack import build_environment, traverse from spack import build_environment, traverse

View File

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

View File

@@ -13,9 +13,9 @@
import spack.config import spack.config
import spack.environment as ev import spack.environment as ev
import spack.error import spack.repo
import spack.schema.env import spack.schema.env
import spack.spec import spack.schema.packages
import spack.store import spack.store
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
from spack.cmd.common import arguments from spack.cmd.common import arguments
@@ -256,7 +256,7 @@ def config_remove(args):
existing.pop(value, None) existing.pop(value, None)
else: else:
# This should be impossible to reach # This should be impossible to reach
raise spack.error.ConfigError("Config has nested non-dict values") raise spack.config.ConfigError("Config has nested non-dict values")
spack.config.set(path, existing, scope) spack.config.set(path, existing, scope)
@@ -340,7 +340,7 @@ def _config_change(config_path, match_spec_str=None):
if not changed: if not changed:
existing_requirements = spack.config.get(key_path) existing_requirements = spack.config.get(key_path)
if isinstance(existing_requirements, str): if isinstance(existing_requirements, str):
raise spack.error.ConfigError( raise spack.config.ConfigError(
"'config change' needs to append a requirement," "'config change' needs to append a requirement,"
" but existing require: config is not a list" " but existing require: config is not a list"
) )
@@ -536,11 +536,11 @@ def config_prefer_upstream(args):
# Get and list all the variants that differ from the default. # Get and list all the variants that differ from the default.
variants = [] variants = []
for var_name, variant in spec.variants.items(): for var_name, variant in spec.variants.items():
if var_name in ["patches"] or not spec.package.has_variant(var_name): if var_name in ["patches"] or var_name not in spec.package.variants:
continue continue
vdef = spec.package.get_variant(var_name) variant_desc, _ = spec.package.variants[var_name]
if variant.value != vdef.default: if variant.value != variant_desc.default:
variants.append(str(variant)) variants.append(str(variant))
variants.sort() variants.sort()
variants = " ".join(variants) variants = " ".join(variants)

View File

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

View File

@@ -13,12 +13,11 @@
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.filesystem import working_dir from llnl.util.filesystem import working_dir
import spack import spack.config
import spack.paths import spack.paths
import spack.platforms import spack.platforms
import spack.spec
import spack.store
import spack.util.git import spack.util.git
from spack.main import get_version
from spack.util.executable import which from spack.util.executable import which
description = "debugging commands for troubleshooting Spack" description = "debugging commands for troubleshooting Spack"
@@ -90,7 +89,7 @@ def report(args):
host_os = host_platform.operating_system("frontend") host_os = host_platform.operating_system("frontend")
host_target = host_platform.target("frontend") host_target = host_platform.target("frontend")
architecture = spack.spec.ArchSpec((str(host_platform), str(host_os), str(host_target))) architecture = spack.spec.ArchSpec((str(host_platform), str(host_os), str(host_target)))
print("* **Spack:**", spack.get_version()) print("* **Spack:**", get_version())
print("* **Python:**", platform.python_version()) print("* **Python:**", platform.python_version())
print("* **Platform:**", architecture) print("* **Platform:**", architecture)

View File

@@ -11,6 +11,7 @@
import spack.cmd import spack.cmd
import spack.environment as ev import spack.environment as ev
import spack.package_base import spack.package_base
import spack.repo
import spack.store import spack.store
from spack.cmd.common import arguments from spack.cmd.common import arguments

View File

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

View File

@@ -8,13 +8,10 @@
import llnl.util.tty as tty import llnl.util.tty as tty
import spack.build_environment
import spack.cmd import spack.cmd
import spack.cmd.common.arguments
import spack.config import spack.config
import spack.repo import spack.repo
from spack.cmd.common import arguments from spack.cmd.common import arguments
from spack.installer import PackageInstaller
description = "developer build: build from code in current working directory" description = "developer build: build from code in current working directory"
section = "build" section = "build"
@@ -132,9 +129,9 @@ def dev_build(self, args):
elif args.test == "root": elif args.test == "root":
tests = [spec.name for spec in specs] tests = [spec.name for spec in specs]
PackageInstaller( spec.package.do_install(
[spec.package],
tests=tests, tests=tests,
make_jobs=args.jobs,
keep_prefix=args.keep_prefix, keep_prefix=args.keep_prefix,
install_deps=not args.ignore_deps, install_deps=not args.ignore_deps,
verbose=not args.quiet, verbose=not args.quiet,
@@ -142,7 +139,7 @@ def dev_build(self, args):
stop_before=args.before, stop_before=args.before,
skip_patch=args.skip_patch, skip_patch=args.skip_patch,
stop_at=args.until, stop_at=args.until,
).install() )
# drop into the build environment of the package? # drop into the build environment of the package?
if args.shell is not None: if args.shell is not None:

View File

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

View File

@@ -12,6 +12,7 @@
import spack.cmd import spack.cmd
import spack.environment as ev import spack.environment as ev
import spack.solver.asp as asp import spack.solver.asp as asp
import spack.util.environment
import spack.util.spack_json as sjson import spack.util.spack_json as sjson
from spack.cmd.common import arguments from spack.cmd.common import arguments

View File

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

View File

@@ -18,9 +18,9 @@
import spack.cray_manifest as cray_manifest import spack.cray_manifest as cray_manifest
import spack.detection import spack.detection
import spack.error import spack.error
import spack.package_base
import spack.repo import spack.repo
import spack.spec import spack.spec
import spack.util.environment
from spack.cmd.common import arguments from spack.cmd.common import arguments
description = "manage external packages in Spack configuration" description = "manage external packages in Spack configuration"

View File

@@ -8,6 +8,7 @@
import spack.cmd import spack.cmd
import spack.config import spack.config
import spack.environment as ev import spack.environment as ev
import spack.repo
import spack.traverse import spack.traverse
from spack.cmd.common import arguments from spack.cmd.common import arguments

View File

@@ -10,11 +10,10 @@
import llnl.util.tty as tty import llnl.util.tty as tty
import llnl.util.tty.color as color import llnl.util.tty.color as color
import spack.bootstrap
import spack.cmd as cmd import spack.cmd as cmd
import spack.config
import spack.environment as ev import spack.environment as ev
import spack.repo import spack.repo
import spack.spec
import spack.store import spack.store
from spack.cmd.common import arguments from spack.cmd.common import arguments
from spack.database import InstallStatuses from spack.database import InstallStatuses

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