Compare commits
394 Commits
v1.0.0-alp
...
packages/c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1e38c62748 | ||
![]() |
9697c1934c | ||
![]() |
a011b49e1e | ||
![]() |
ae50757f3c | ||
![]() |
6f1dce95f9 | ||
![]() |
fd59d3e589 | ||
![]() |
0172208c52 | ||
![]() |
02c2516e88 | ||
![]() |
a8e37ccbbb | ||
![]() |
f0f463c8dc | ||
![]() |
a137da1cd5 | ||
![]() |
03e972314f | ||
![]() |
9a7a3d2743 | ||
![]() |
76f00a3659 | ||
![]() |
cd98781fb4 | ||
![]() |
dd16f451fc | ||
![]() |
4f6cd5abde | ||
![]() |
d7f05e08be | ||
![]() |
9747978c7f | ||
![]() |
f043455ccc | ||
![]() |
fb9d6427e6 | ||
![]() |
76e83e10c1 | ||
![]() |
af89bdf632 | ||
![]() |
46f5b192ef | ||
![]() |
18cd922aab | ||
![]() |
5518ad9611 | ||
![]() |
57a1807443 | ||
![]() |
3909308d5c | ||
![]() |
54210270c8 | ||
![]() |
1a71bb046e | ||
![]() |
dbd6857d32 | ||
![]() |
025bc24996 | ||
![]() |
01e16b58a3 | ||
![]() |
f71e202f24 | ||
![]() |
f7edd10c17 | ||
![]() |
153c0805dd | ||
![]() |
5d8517ef69 | ||
![]() |
f23cae6a86 | ||
![]() |
e6e67f8e0a | ||
![]() |
e6bef4ca9b | ||
![]() |
e3e0bef0de | ||
![]() |
42486d93ec | ||
![]() |
6d608a9664 | ||
![]() |
04313afc63 | ||
![]() |
f839d2ba56 | ||
![]() |
2b1a8b1913 | ||
![]() |
8907003648 | ||
![]() |
8afdba4bf7 | ||
![]() |
57cabbfb10 | ||
![]() |
c71efb9040 | ||
![]() |
c5dd2d43d2 | ||
![]() |
34338ef757 | ||
![]() |
c0bdc37226 | ||
![]() |
8bad9fb804 | ||
![]() |
2df7cc0087 | ||
![]() |
40d40ccc52 | ||
![]() |
afe7d6c39e | ||
![]() |
113733d9fb | ||
![]() |
a8e2da5bb8 | ||
![]() |
97750189b6 | ||
![]() |
bcd40835a0 | ||
![]() |
2c3f2c5733 | ||
![]() |
302d74394b | ||
![]() |
cf94dc7823 | ||
![]() |
4411ee3382 | ||
![]() |
f790ce0f72 | ||
![]() |
64d53037db | ||
![]() |
4aef50739b | ||
![]() |
a6e966f6f2 | ||
![]() |
1f428c4188 | ||
![]() |
731e48b1bd | ||
![]() |
74ff9ad821 | ||
![]() |
16a4eff689 | ||
![]() |
d0b0d8db50 | ||
![]() |
54f591cce5 | ||
![]() |
8677bb4d43 | ||
![]() |
b66b80a96a | ||
![]() |
10e21f399c | ||
![]() |
56892f6140 | ||
![]() |
7eddc4b1f8 | ||
![]() |
3c7392bbcc | ||
![]() |
bb0517f4d9 | ||
![]() |
c8994ee50f | ||
![]() |
4b2f5638f2 | ||
![]() |
31312a379f | ||
![]() |
b0d5f272b0 | ||
![]() |
1c93fef160 | ||
![]() |
8bb5f4faf4 | ||
![]() |
f76ab5f72f | ||
![]() |
49c831edc3 | ||
![]() |
c943c8c1d2 | ||
![]() |
e0e6f29584 | ||
![]() |
72bc3bb803 | ||
![]() |
dba8fe2b96 | ||
![]() |
4487598d60 | ||
![]() |
495537cf56 | ||
![]() |
22c3b4099f | ||
![]() |
13978d11a0 | ||
![]() |
a22114b20b | ||
![]() |
c10624390f | ||
![]() |
fb3d9de80b | ||
![]() |
fbb688af07 | ||
![]() |
d34b709425 | ||
![]() |
cb0b188cf6 | ||
![]() |
9a2b0aca66 | ||
![]() |
89a8ab3233 | ||
![]() |
5d87166c07 | ||
![]() |
15c989b3fe | ||
![]() |
b7f556e4b4 | ||
![]() |
36f32ceda3 | ||
![]() |
01d77ed915 | ||
![]() |
0049f8332d | ||
![]() |
39c10c3116 | ||
![]() |
71d1901831 | ||
![]() |
41e0863b86 | ||
![]() |
a75d83f65c | ||
![]() |
f2f13964fb | ||
![]() |
9b032018d6 | ||
![]() |
7d470c05be | ||
![]() |
664fe9e9e6 | ||
![]() |
2745a519e2 | ||
![]() |
4348ee1c75 | ||
![]() |
8e39fb1e54 | ||
![]() |
09458312a3 | ||
![]() |
5fd0693df4 | ||
![]() |
f58684429d | ||
![]() |
409611a479 | ||
![]() |
dd98cfb839 | ||
![]() |
5c91667dab | ||
![]() |
9efd6f3f11 | ||
![]() |
a8f5289801 | ||
![]() |
ac635aa777 | ||
![]() |
45dcddf9c3 | ||
![]() |
f1660722e7 | ||
![]() |
04b44d841c | ||
![]() |
7f30502297 | ||
![]() |
61b1586c51 | ||
![]() |
8579efcadf | ||
![]() |
1c3e2b5425 | ||
![]() |
011ef0aaaf | ||
![]() |
9642f3f49a | ||
![]() |
a6c9b55fad | ||
![]() |
608ed967e1 | ||
![]() |
742eaa32b7 | ||
![]() |
763b35a2e0 | ||
![]() |
12280f864c | ||
![]() |
253ba05732 | ||
![]() |
195b869e1c | ||
![]() |
393961ffd6 | ||
![]() |
392a58e9be | ||
![]() |
0e8e97a811 | ||
![]() |
43a0cbe7a2 | ||
![]() |
bb35a98079 | ||
![]() |
fa7e0e8230 | ||
![]() |
2c128751f5 | ||
![]() |
fb0493a366 | ||
![]() |
6d1b6e7087 | ||
![]() |
759518182c | ||
![]() |
7ebabfcf0e | ||
![]() |
6203ae31d2 | ||
![]() |
6b13017ded | ||
![]() |
2c51b5853f | ||
![]() |
d0cbd056a8 | ||
![]() |
e1b579a8b4 | ||
![]() |
b02dcf697d | ||
![]() |
6e046b04c7 | ||
![]() |
d196795437 | ||
![]() |
0d444fb4e7 | ||
![]() |
467e631260 | ||
![]() |
f21de698f7 | ||
![]() |
59532986be | ||
![]() |
36fd547b40 | ||
![]() |
b5f9dea6d0 | ||
![]() |
5904834295 | ||
![]() |
2da8a1d1e3 | ||
![]() |
d50eba40d9 | ||
![]() |
8d3a733b77 | ||
![]() |
dfa86dce08 | ||
![]() |
3d82e5c573 | ||
![]() |
a77f903f4d | ||
![]() |
92260b179d | ||
![]() |
196c912b8a | ||
![]() |
0f54995e53 | ||
![]() |
9d1332f1a1 | ||
![]() |
40a1da4a73 | ||
![]() |
82e091e2c2 | ||
![]() |
c86112b0e8 | ||
![]() |
bb25c04845 | ||
![]() |
d69d26d9ce | ||
![]() |
06d660b9ba | ||
![]() |
40b3196412 | ||
![]() |
7e893da4a6 | ||
![]() |
13aa8b6867 | ||
![]() |
b0afb619de | ||
![]() |
7a82c703c7 | ||
![]() |
0d3667175a | ||
![]() |
a754341f6c | ||
![]() |
a50c45f00c | ||
![]() |
87e65e5377 | ||
![]() |
50fe96aaf6 | ||
![]() |
56495a8cd8 | ||
![]() |
c054cb818d | ||
![]() |
bc28ec35d1 | ||
![]() |
e47a6059a7 | ||
![]() |
0d170b9ef3 | ||
![]() |
5174cb9180 | ||
![]() |
22ba366e85 | ||
![]() |
13558269b5 | ||
![]() |
615b7a6ddb | ||
![]() |
0415b21d3d | ||
![]() |
053c9d2846 | ||
![]() |
1e763629f6 | ||
![]() |
7568687f1e | ||
![]() |
3b81c0e6b7 | ||
![]() |
c764400338 | ||
![]() |
4e8a6eec1a | ||
![]() |
ebc9f03dda | ||
![]() |
8ac0bd2825 | ||
![]() |
cc9e0137df | ||
![]() |
b8e448afa0 | ||
![]() |
209d670bf3 | ||
![]() |
c6202842ed | ||
![]() |
b2a75db030 | ||
![]() |
0ec00a9c9a | ||
![]() |
5e3020ad02 | ||
![]() |
a0d0e6321f | ||
![]() |
0afac0beaa | ||
![]() |
6155be8548 | ||
![]() |
611cb98b02 | ||
![]() |
ea5742853f | ||
![]() |
25a3e8ba59 | ||
![]() |
7fbb3df6b0 | ||
![]() |
a728db95de | ||
![]() |
7bc4069b9e | ||
![]() |
51fc195d14 | ||
![]() |
27a0593104 | ||
![]() |
f95e27a159 | ||
![]() |
effe433c96 | ||
![]() |
21988fbb18 | ||
![]() |
2db654bf5a | ||
![]() |
9992b563db | ||
![]() |
daba1a805e | ||
![]() |
832bf95aa4 | ||
![]() |
81e6dcd95c | ||
![]() |
518572e710 | ||
![]() |
6f4ac31a67 | ||
![]() |
e291daaa17 | ||
![]() |
58f1e791a0 | ||
![]() |
aba0a740c2 | ||
![]() |
0fe8e763c3 | ||
![]() |
0e2d261b7e | ||
![]() |
85cb234861 | ||
![]() |
87a83db623 | ||
![]() |
e1e17786c5 | ||
![]() |
68af5cc4c0 | ||
![]() |
70df460fa7 | ||
![]() |
31a1b2fd6c | ||
![]() |
f8fd51e12f | ||
![]() |
12784594aa | ||
![]() |
e0eb0aba37 | ||
![]() |
f47bf5f6b8 | ||
![]() |
9296527775 | ||
![]() |
08c53fa405 | ||
![]() |
0c6f0c090d | ||
![]() |
c623448f81 | ||
![]() |
df71341972 | ||
![]() |
75862c456d | ||
![]() |
e680a0c153 | ||
![]() |
9ad36080ca | ||
![]() |
ecd14f0ad9 | ||
![]() |
c44edf1e8d | ||
![]() |
1eacdca5aa | ||
![]() |
4a8f5efb38 | ||
![]() |
2e753571bd | ||
![]() |
da16336550 | ||
![]() |
1818e70e74 | ||
![]() |
1dde785e9a | ||
![]() |
a7af32c23b | ||
![]() |
6c92ad439b | ||
![]() |
93f555eb14 | ||
![]() |
fa3725e9de | ||
![]() |
870dd6206f | ||
![]() |
b1d411ab06 | ||
![]() |
783eccfbd5 | ||
![]() |
a842332b1b | ||
![]() |
7e41288ca6 | ||
![]() |
3bb375a47f | ||
![]() |
478855728f | ||
![]() |
5e3baeabfa | ||
![]() |
58b9b54066 | ||
![]() |
3918deab74 | ||
![]() |
ceb2ce352f | ||
![]() |
7dc6bff7b1 | ||
![]() |
05fbbd7164 | ||
![]() |
58421866c2 | ||
![]() |
962498095d | ||
![]() |
d0217cf04e | ||
![]() |
65745fa0df | ||
![]() |
9f7cff1780 | ||
![]() |
bb43fa5444 | ||
![]() |
847f560a6e | ||
![]() |
623ff835fc | ||
![]() |
ca19790ff2 | ||
![]() |
f23366e4f8 | ||
![]() |
42fb689501 | ||
![]() |
c33bbdb77d | ||
![]() |
1af6aa22c1 | ||
![]() |
03d9373e5c | ||
![]() |
f469a3d6ab | ||
![]() |
25f24d947a | ||
![]() |
af25a84a56 | ||
![]() |
59a71959e7 | ||
![]() |
00e804a94b | ||
![]() |
db997229f2 | ||
![]() |
6fac041d40 | ||
![]() |
f8b2c65ddf | ||
![]() |
c504304d39 | ||
![]() |
976f1c2198 | ||
![]() |
e7c591a8b8 | ||
![]() |
f3522cba74 | ||
![]() |
0bd9c235a0 | ||
![]() |
335fca7049 | ||
![]() |
ce5ef14fdb | ||
![]() |
2447d16e55 | ||
![]() |
8196c68ff3 | ||
![]() |
308f74fe8b | ||
![]() |
864f09fef0 | ||
![]() |
29b53581e2 | ||
![]() |
e441e780b9 | ||
![]() |
4c642df5ae | ||
![]() |
217774c972 | ||
![]() |
8ec89fc54c | ||
![]() |
66ce93a2e3 | ||
![]() |
116ffe5809 | ||
![]() |
6b0ea2db1d | ||
![]() |
d72b371c8a | ||
![]() |
aa88ced154 | ||
![]() |
d89ae7bcde | ||
![]() |
53d1665a8b | ||
![]() |
9fa1654102 | ||
![]() |
2c692a5755 | ||
![]() |
c0df012b18 | ||
![]() |
66e8523e14 | ||
![]() |
3932299768 | ||
![]() |
3eba6b8379 | ||
![]() |
369928200a | ||
![]() |
81ed0f8d87 | ||
![]() |
194b6311e9 | ||
![]() |
8420898f79 | ||
![]() |
f556ba46d9 | ||
![]() |
ddaa9d5d81 | ||
![]() |
b878fe5555 | ||
![]() |
b600bfc779 | ||
![]() |
612c289c41 | ||
![]() |
e42c76cccf | ||
![]() |
25013bacf2 | ||
![]() |
3d554db198 | ||
![]() |
b6def50dcb | ||
![]() |
bf591c96bd | ||
![]() |
edf1d2ec40 | ||
![]() |
07f607ec9f | ||
![]() |
93747c5e24 | ||
![]() |
b746d4596a | ||
![]() |
8814705936 | ||
![]() |
c989541ebc | ||
![]() |
1759ce05dd | ||
![]() |
c0c1a4aea1 | ||
![]() |
53353ae64e | ||
![]() |
62f7a4c9b1 | ||
![]() |
39679d0882 | ||
![]() |
50e6bf9979 | ||
![]() |
b874c31cc8 | ||
![]() |
04baad90f5 | ||
![]() |
1022527923 | ||
![]() |
7ef19ec1d8 | ||
![]() |
6e45b51f27 | ||
![]() |
5f9cd0991b | ||
![]() |
98c44fc351 | ||
![]() |
b99f850c8e | ||
![]() |
cbbd68d16b | ||
![]() |
e4fbf99497 | ||
![]() |
6a225d5405 | ||
![]() |
af9fd82476 | ||
![]() |
29c1152484 | ||
![]() |
d6a8af6a1d | ||
![]() |
3c3dad0a7a | ||
![]() |
109efdff88 | ||
![]() |
fa318e2c92 | ||
![]() |
064e70990d | ||
![]() |
c40139b7d6 | ||
![]() |
c302e1a768 | ||
![]() |
7171015f1c | ||
![]() |
8ab6f33eb6 | ||
![]() |
a66ab9cc6c |
18
.github/workflows/build-containers.yml
vendored
18
.github/workflows/build-containers.yml
vendored
@@ -40,17 +40,17 @@ jobs:
|
|||||||
# 1: Platforms to build for
|
# 1: Platforms to build for
|
||||||
# 2: Base image (e.g. ubuntu:22.04)
|
# 2: Base image (e.g. ubuntu:22.04)
|
||||||
dockerfile: [[amazon-linux, 'linux/amd64,linux/arm64', 'amazonlinux:2'],
|
dockerfile: [[amazon-linux, 'linux/amd64,linux/arm64', 'amazonlinux:2'],
|
||||||
[centos-stream9, 'linux/amd64,linux/arm64,linux/ppc64le', 'centos:stream9'],
|
[centos-stream9, 'linux/amd64,linux/arm64', 'centos:stream9'],
|
||||||
[leap15, 'linux/amd64,linux/arm64,linux/ppc64le', 'opensuse/leap:15'],
|
[leap15, 'linux/amd64,linux/arm64', 'opensuse/leap:15'],
|
||||||
[ubuntu-focal, 'linux/amd64,linux/arm64,linux/ppc64le', 'ubuntu:20.04'],
|
[ubuntu-focal, 'linux/amd64,linux/arm64', 'ubuntu:20.04'],
|
||||||
[ubuntu-jammy, 'linux/amd64,linux/arm64,linux/ppc64le', 'ubuntu:22.04'],
|
[ubuntu-jammy, 'linux/amd64,linux/arm64', 'ubuntu:22.04'],
|
||||||
[ubuntu-noble, 'linux/amd64,linux/arm64,linux/ppc64le', 'ubuntu:24.04'],
|
[ubuntu-noble, 'linux/amd64,linux/arm64', 'ubuntu:24.04'],
|
||||||
[almalinux8, 'linux/amd64,linux/arm64,linux/ppc64le', 'almalinux:8'],
|
[almalinux8, 'linux/amd64,linux/arm64', 'almalinux:8'],
|
||||||
[almalinux9, 'linux/amd64,linux/arm64,linux/ppc64le', 'almalinux:9'],
|
[almalinux9, 'linux/amd64,linux/arm64', 'almalinux:9'],
|
||||||
[rockylinux8, 'linux/amd64,linux/arm64', 'rockylinux:8'],
|
[rockylinux8, 'linux/amd64,linux/arm64', 'rockylinux:8'],
|
||||||
[rockylinux9, 'linux/amd64,linux/arm64', 'rockylinux:9'],
|
[rockylinux9, 'linux/amd64,linux/arm64', 'rockylinux:9'],
|
||||||
[fedora39, 'linux/amd64,linux/arm64,linux/ppc64le', 'fedora:39'],
|
[fedora39, 'linux/amd64,linux/arm64', 'fedora:39'],
|
||||||
[fedora40, 'linux/amd64,linux/arm64,linux/ppc64le', 'fedora:40']]
|
[fedora40, 'linux/amd64,linux/arm64', 'fedora:40']]
|
||||||
name: Build ${{ matrix.dockerfile[0] }}
|
name: Build ${{ matrix.dockerfile[0] }}
|
||||||
if: github.repository == 'spack/spack'
|
if: github.repository == 'spack/spack'
|
||||||
steps:
|
steps:
|
||||||
|
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@@ -81,6 +81,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
with_coverage: ${{ needs.changes.outputs.core }}
|
with_coverage: ${{ needs.changes.outputs.core }}
|
||||||
|
|
||||||
|
import-check:
|
||||||
|
needs: [ changes ]
|
||||||
|
uses: ./.github/workflows/import-check.yaml
|
||||||
|
|
||||||
all-prechecks:
|
all-prechecks:
|
||||||
needs: [ prechecks ]
|
needs: [ prechecks ]
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
|
1
.github/workflows/coverage.yml
vendored
1
.github/workflows/coverage.yml
vendored
@@ -33,3 +33,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
verbose: true
|
verbose: true
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
49
.github/workflows/import-check.yaml
vendored
Normal file
49
.github/workflows/import-check.yaml
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
name: import-check
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Check we don't make the situation with circular imports worse
|
||||||
|
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@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
|
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@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
|
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@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
|
with:
|
||||||
|
path: new
|
||||||
|
- name: Install circular import checker
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
|
with:
|
||||||
|
repository: haampie/circular-import-fighter
|
||||||
|
ref: 4cdb0bf15f04ab6b49041d5ef1bfd9644cce7f33
|
||||||
|
path: circular-import-fighter
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: circular-import-fighter
|
||||||
|
run: make -j dependencies
|
||||||
|
- name: Circular import check
|
||||||
|
working-directory: circular-import-fighter
|
||||||
|
run: make -j compare "SPACK_ROOT=../old ../new"
|
60
.github/workflows/valid-style.yml
vendored
60
.github/workflows/valid-style.yml
vendored
@@ -86,66 +86,6 @@ jobs:
|
|||||||
spack -d bootstrap now --dev
|
spack -d bootstrap now --dev
|
||||||
spack -d style -t black
|
spack -d style -t black
|
||||||
spack unit-test -V
|
spack unit-test -V
|
||||||
# Check we don't make the situation with circular imports worse
|
|
||||||
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@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
||||||
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@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
||||||
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@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
||||||
with:
|
|
||||||
path: new
|
|
||||||
- name: Install circular import checker
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
|
||||||
with:
|
|
||||||
repository: haampie/circular-import-fighter
|
|
||||||
ref: b5d6ce9be35f602cca7d5a6aa0259fca10639cca
|
|
||||||
path: circular-import-fighter
|
|
||||||
- name: Install dependencies
|
|
||||||
working-directory: circular-import-fighter
|
|
||||||
run: make -j dependencies
|
|
||||||
- name: Problematic imports before
|
|
||||||
working-directory: circular-import-fighter
|
|
||||||
run: make SPACK_ROOT=../old SUFFIX=.old
|
|
||||||
- name: Problematic imports after
|
|
||||||
working-directory: circular-import-fighter
|
|
||||||
run: make SPACK_ROOT=../new SUFFIX=.new
|
|
||||||
- name: Compare import cycles
|
|
||||||
working-directory: circular-import-fighter
|
|
||||||
run: |
|
|
||||||
edges_before="$(head -n1 solution.old)"
|
|
||||||
edges_after="$(head -n1 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"Problematic imports before"\033[0m and '
|
|
||||||
printf '\033[1;97m"Problematic imports after"\033[0m.\n'
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
printf '\033[1;32mImport check passed: %s <= %s\033[0m\n' "$edges_after" "$edges_before"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Further style checks from pylint
|
# Further style checks from pylint
|
||||||
pylint:
|
pylint:
|
||||||
|
@@ -25,7 +25,6 @@ exit 1
|
|||||||
# The code above runs this file with our preferred python interpreter.
|
# The code above runs this file with our preferred python interpreter.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
min_python3 = (3, 6)
|
min_python3 = (3, 6)
|
||||||
|
@@ -63,3 +63,7 @@ concretizer:
|
|||||||
# Setting this to false yields unreproducible results, so we advise to use that value only
|
# Setting this to false yields unreproducible results, so we advise to use that value only
|
||||||
# for debugging purposes (e.g. check which constraints can help Spack concretize faster).
|
# for debugging purposes (e.g. check which constraints can help Spack concretize faster).
|
||||||
error_on_timeout: true
|
error_on_timeout: true
|
||||||
|
|
||||||
|
# Static analysis may reduce the concretization time by generating smaller ASP problems, in
|
||||||
|
# cases where there are requirements that prevent part of the search space to be explored.
|
||||||
|
static_analysis: false
|
||||||
|
@@ -36,7 +36,7 @@ packages:
|
|||||||
go-or-gccgo-bootstrap: [go-bootstrap, gcc]
|
go-or-gccgo-bootstrap: [go-bootstrap, gcc]
|
||||||
iconv: [libiconv]
|
iconv: [libiconv]
|
||||||
ipp: [intel-oneapi-ipp]
|
ipp: [intel-oneapi-ipp]
|
||||||
java: [openjdk, jdk, ibm-java]
|
java: [openjdk, jdk]
|
||||||
jpeg: [libjpeg-turbo, libjpeg]
|
jpeg: [libjpeg-turbo, libjpeg]
|
||||||
lapack: [openblas, amdlibflame]
|
lapack: [openblas, amdlibflame]
|
||||||
libc: [glibc, musl]
|
libc: [glibc, musl]
|
||||||
@@ -73,15 +73,27 @@ packages:
|
|||||||
permissions:
|
permissions:
|
||||||
read: world
|
read: world
|
||||||
write: user
|
write: user
|
||||||
|
cray-fftw:
|
||||||
|
buildable: false
|
||||||
|
cray-libsci:
|
||||||
|
buildable: false
|
||||||
cray-mpich:
|
cray-mpich:
|
||||||
buildable: false
|
buildable: false
|
||||||
cray-mvapich2:
|
cray-mvapich2:
|
||||||
buildable: false
|
buildable: false
|
||||||
|
cray-pmi:
|
||||||
|
buildable: false
|
||||||
egl:
|
egl:
|
||||||
buildable: false
|
buildable: false
|
||||||
|
essl:
|
||||||
|
buildable: false
|
||||||
fujitsu-mpi:
|
fujitsu-mpi:
|
||||||
buildable: false
|
buildable: false
|
||||||
|
fujitsu-ssl2:
|
||||||
|
buildable: false
|
||||||
hpcx-mpi:
|
hpcx-mpi:
|
||||||
buildable: false
|
buildable: false
|
||||||
|
mpt:
|
||||||
|
buildable: false
|
||||||
spectrum-mpi:
|
spectrum-mpi:
|
||||||
buildable: false
|
buildable: false
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
config:
|
config:
|
||||||
locks: false
|
locks: false
|
||||||
build_stage::
|
build_stage::
|
||||||
- '$spack/.staging'
|
- '$user_cache_path/stage'
|
||||||
stage_name: '{name}-{version}-{hash:7}'
|
stage_name: '{name}-{version}-{hash:7}'
|
||||||
|
@@ -170,7 +170,7 @@ bootstrapping.
|
|||||||
To register the mirror on the platform where it's supposed to be used run the following command(s):
|
To register the mirror on the platform where it's supposed to be used run the following command(s):
|
||||||
% spack bootstrap add --trust local-sources /opt/bootstrap/metadata/sources
|
% spack bootstrap add --trust local-sources /opt/bootstrap/metadata/sources
|
||||||
% spack bootstrap add --trust local-binaries /opt/bootstrap/metadata/binaries
|
% spack bootstrap add --trust local-binaries /opt/bootstrap/metadata/binaries
|
||||||
|
% spack buildcache update-index /opt/bootstrap/bootstrap_cache
|
||||||
|
|
||||||
This command needs to be run on a machine with internet access and the resulting folder
|
This command needs to be run on a machine with internet access and the resulting folder
|
||||||
has to be moved over to the air-gapped system. Once the local sources are added using the
|
has to be moved over to the air-gapped system. Once the local sources are added using the
|
||||||
|
@@ -272,9 +272,9 @@ often lists dependencies and the flags needed to locate them. The
|
|||||||
"environment variables" section lists environment variables that the
|
"environment variables" section lists environment variables that the
|
||||||
build system uses to pass flags to the compiler and linker.
|
build system uses to pass flags to the compiler and linker.
|
||||||
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Addings flags to configure
|
Adding flags to configure
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
For most of the flags you encounter, you will want a variant to
|
For most of the flags you encounter, you will want a variant to
|
||||||
optionally enable/disable them. You can then optionally pass these
|
optionally enable/disable them. You can then optionally pass these
|
||||||
@@ -285,7 +285,7 @@ function like so:
|
|||||||
|
|
||||||
def configure_args(self):
|
def configure_args(self):
|
||||||
args = []
|
args = []
|
||||||
|
...
|
||||||
if self.spec.satisfies("+mpi"):
|
if self.spec.satisfies("+mpi"):
|
||||||
args.append("--enable-mpi")
|
args.append("--enable-mpi")
|
||||||
else:
|
else:
|
||||||
@@ -299,7 +299,10 @@ Alternatively, you can use the :ref:`enable_or_disable <autotools_enable_or_dis
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def configure_args(self):
|
def configure_args(self):
|
||||||
return [self.enable_or_disable("mpi")]
|
args = []
|
||||||
|
...
|
||||||
|
args.extend(self.enable_or_disable("mpi"))
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
Note that we are explicitly disabling MPI support if it is not
|
Note that we are explicitly disabling MPI support if it is not
|
||||||
@@ -344,7 +347,14 @@ typically used to enable or disable some feature within the package.
|
|||||||
default=False,
|
default=False,
|
||||||
description="Memchecker support for debugging [degrades performance]"
|
description="Memchecker support for debugging [degrades performance]"
|
||||||
)
|
)
|
||||||
config_args.extend(self.enable_or_disable("memchecker"))
|
...
|
||||||
|
|
||||||
|
def configure_args(self):
|
||||||
|
args = []
|
||||||
|
...
|
||||||
|
args.extend(self.enable_or_disable("memchecker"))
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
In this example, specifying the variant ``+memchecker`` will generate
|
In this example, specifying the variant ``+memchecker`` will generate
|
||||||
the following configuration options:
|
the following configuration options:
|
||||||
|
@@ -56,13 +56,13 @@ If you look at the ``perl`` package, you'll see:
|
|||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
phases = ["configure", "build", "install"]
|
phases = ("configure", "build", "install")
|
||||||
|
|
||||||
Similarly, ``cmake`` defines:
|
Similarly, ``cmake`` defines:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
phases = ["bootstrap", "build", "install"]
|
phases = ("bootstrap", "build", "install")
|
||||||
|
|
||||||
If we look at the ``cmake`` example, this tells Spack's ``PackageBase``
|
If we look at the ``cmake`` example, this tells Spack's ``PackageBase``
|
||||||
class to run the ``bootstrap``, ``build``, and ``install`` functions
|
class to run the ``bootstrap``, ``build``, and ``install`` functions
|
||||||
|
@@ -361,7 +361,6 @@ and the tags associated with the class of runners to build on.
|
|||||||
* ``.linux_neoverse_n1``
|
* ``.linux_neoverse_n1``
|
||||||
* ``.linux_neoverse_v1``
|
* ``.linux_neoverse_v1``
|
||||||
* ``.linux_neoverse_v2``
|
* ``.linux_neoverse_v2``
|
||||||
* ``.linux_power``
|
|
||||||
* ``.linux_skylake``
|
* ``.linux_skylake``
|
||||||
* ``.linux_x86_64``
|
* ``.linux_x86_64``
|
||||||
* ``.linux_x86_64_v4``
|
* ``.linux_x86_64_v4``
|
||||||
|
@@ -543,10 +543,10 @@ With either interpreter you can run a single command:
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ spack python -c 'from spack.spec import Spec; Spec("python").concretized()'
|
$ spack python -c 'from spack.concretize import concretize_one; concretize_one("python")'
|
||||||
...
|
...
|
||||||
|
|
||||||
$ spack python -i ipython -c 'from spack.spec import Spec; Spec("python").concretized()'
|
$ spack python -i ipython -c 'from spack.concretize import concretize_one; concretize_one("python")'
|
||||||
Out[1]: ...
|
Out[1]: ...
|
||||||
|
|
||||||
or a file:
|
or a file:
|
||||||
|
@@ -112,6 +112,19 @@ the original but may concretize differently in the presence of different
|
|||||||
explicit or default configuration settings (e.g., a different version of
|
explicit or default configuration settings (e.g., a different version of
|
||||||
Spack or for a different user account).
|
Spack or for a different user account).
|
||||||
|
|
||||||
|
Environments created from a manifest will copy any included configs
|
||||||
|
from relative paths inside the environment. Relative paths from
|
||||||
|
outside the environment will cause errors, and absolute paths will be
|
||||||
|
kept absolute. For example, if ``spack.yaml`` includes:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
spack:
|
||||||
|
include: [./config.yaml]
|
||||||
|
|
||||||
|
then the created environment will have its own copy of the file
|
||||||
|
``config.yaml`` copied from the location in the original environment.
|
||||||
|
|
||||||
Create an environment from a ``spack.lock`` file using:
|
Create an environment from a ``spack.lock`` file using:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
@@ -160,7 +173,7 @@ accepts. If an environment already exists then spack will simply activate it
|
|||||||
and ignore the create-specific flags.
|
and ignore the create-specific flags.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ spack env activate --create -p myenv
|
$ spack env activate --create -p myenv
|
||||||
# ...
|
# ...
|
||||||
# [creates if myenv does not exist yet]
|
# [creates if myenv does not exist yet]
|
||||||
@@ -424,8 +437,8 @@ Developing Packages in a Spack Environment
|
|||||||
|
|
||||||
The ``spack develop`` command allows one to develop Spack packages in
|
The ``spack develop`` command allows one to develop Spack packages in
|
||||||
an environment. It requires a spec containing a concrete version, and
|
an environment. It requires a spec containing a concrete version, and
|
||||||
will configure Spack to install the package from local source.
|
will configure Spack to install the package from local source.
|
||||||
If a version is not provided from the command line interface then spack
|
If a version is not provided from the command line interface then spack
|
||||||
will automatically pick the highest version the package has defined.
|
will automatically pick the highest version the package has defined.
|
||||||
This means any infinity versions (``develop``, ``main``, ``stable``) will be
|
This means any infinity versions (``develop``, ``main``, ``stable``) will be
|
||||||
preferred in this selection process.
|
preferred in this selection process.
|
||||||
@@ -435,9 +448,9 @@ 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's native implementation to check for modifications
|
||||||
is to check if ``mtime`` is newer than the installation.
|
is to check if ``mtime`` is newer than the installation.
|
||||||
A custom check can be created by overriding the ``detect_dev_src_change`` method
|
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
|
in your package class. This is particularly useful for projects using custom spack repo's
|
||||||
to drive development and want to optimize performance.
|
to drive development and want to optimize performance.
|
||||||
|
|
||||||
Spack ensures that all instances of a
|
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
|
||||||
@@ -453,7 +466,7 @@ Further development on ``foo`` can be tested by re-installing the environment,
|
|||||||
and eventually committed and pushed to the upstream git repo.
|
and eventually committed and pushed to the upstream git repo.
|
||||||
|
|
||||||
If the package being developed supports out-of-source builds then users can use the
|
If the package being developed supports out-of-source builds then users can use the
|
||||||
``--build_directory`` flag to control the location and name of the build directory.
|
``--build_directory`` flag to control the location and name of the build directory.
|
||||||
This is a shortcut to set the ``package_attributes:build_directory`` in the
|
This is a shortcut to set the ``package_attributes:build_directory`` in the
|
||||||
``packages`` configuration (see :ref:`assigning-package-attributes`).
|
``packages`` configuration (see :ref:`assigning-package-attributes`).
|
||||||
The supplied location will become the build-directory for that package in all future builds.
|
The supplied location will become the build-directory for that package in all future builds.
|
||||||
|
@@ -456,14 +456,13 @@ For instance, the following config options,
|
|||||||
tcl:
|
tcl:
|
||||||
all:
|
all:
|
||||||
suffixes:
|
suffixes:
|
||||||
^python@3: 'python{^python.version}'
|
^python@3: 'python{^python.version.up_to_2}'
|
||||||
^openblas: 'openblas'
|
^openblas: 'openblas'
|
||||||
|
|
||||||
will add a ``python-3.12.1`` version string to any packages compiled with
|
will add a ``python3.12`` to module names of packages compiled with Python 3.12, and similarly for
|
||||||
Python matching the spec, ``python@3``. This is useful to know which
|
all specs depending on ``python@3``. This is useful to know which version of Python a set of Python
|
||||||
version of Python a set of Python extensions is associated with. Likewise, the
|
extensions is associated with. Likewise, the ``openblas`` string is attached to any program that
|
||||||
``openblas`` string is attached to any program that has openblas in the spec,
|
has openblas in the spec, most likely via the ``+blas`` variant specification.
|
||||||
most likely via the ``+blas`` variant specification.
|
|
||||||
|
|
||||||
The most heavyweight solution to module naming is to change the entire
|
The most heavyweight solution to module naming is to change the entire
|
||||||
naming convention for module files. This uses the projections format
|
naming convention for module files. This uses the projections format
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
"""URL primitives that just require Python standard library."""
|
"""URL primitives that just require Python standard library."""
|
||||||
import itertools
|
import itertools
|
||||||
import os.path
|
import os
|
||||||
import re
|
import re
|
||||||
from typing import Optional, Set, Tuple
|
from typing import Optional, Set, Tuple
|
||||||
from urllib.parse import urlsplit, urlunsplit
|
from urllib.parse import urlsplit, urlunsplit
|
||||||
|
@@ -75,7 +75,6 @@
|
|||||||
"install_tree",
|
"install_tree",
|
||||||
"is_exe",
|
"is_exe",
|
||||||
"join_path",
|
"join_path",
|
||||||
"last_modification_time_recursive",
|
|
||||||
"library_extensions",
|
"library_extensions",
|
||||||
"mkdirp",
|
"mkdirp",
|
||||||
"partition_path",
|
"partition_path",
|
||||||
@@ -669,7 +668,7 @@ def copy(src, dest, _permissions=False):
|
|||||||
_permissions (bool): for internal use only
|
_permissions (bool): for internal use only
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
IOError: if *src* does not match any files or directories
|
OSError: if *src* does not match any files or directories
|
||||||
ValueError: if *src* matches multiple files but *dest* is
|
ValueError: if *src* matches multiple files but *dest* is
|
||||||
not a directory
|
not a directory
|
||||||
"""
|
"""
|
||||||
@@ -680,7 +679,7 @@ def copy(src, dest, _permissions=False):
|
|||||||
|
|
||||||
files = glob.glob(src)
|
files = glob.glob(src)
|
||||||
if not files:
|
if not files:
|
||||||
raise IOError("No such file or directory: '{0}'".format(src))
|
raise OSError("No such file or directory: '{0}'".format(src))
|
||||||
if len(files) > 1 and not os.path.isdir(dest):
|
if len(files) > 1 and not os.path.isdir(dest):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"'{0}' matches multiple files but '{1}' is not a directory".format(src, dest)
|
"'{0}' matches multiple files but '{1}' is not a directory".format(src, dest)
|
||||||
@@ -711,7 +710,7 @@ def install(src, dest):
|
|||||||
dest (str): the destination file or directory
|
dest (str): the destination file or directory
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
IOError: if *src* does not match any files or directories
|
OSError: if *src* does not match any files or directories
|
||||||
ValueError: if *src* matches multiple files but *dest* is
|
ValueError: if *src* matches multiple files but *dest* is
|
||||||
not a directory
|
not a directory
|
||||||
"""
|
"""
|
||||||
@@ -749,7 +748,7 @@ def copy_tree(
|
|||||||
_permissions (bool): for internal use only
|
_permissions (bool): for internal use only
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
IOError: if *src* does not match any files or directories
|
OSError: if *src* does not match any files or directories
|
||||||
ValueError: if *src* is a parent directory of *dest*
|
ValueError: if *src* is a parent directory of *dest*
|
||||||
"""
|
"""
|
||||||
if _permissions:
|
if _permissions:
|
||||||
@@ -763,7 +762,7 @@ def copy_tree(
|
|||||||
|
|
||||||
files = glob.glob(src)
|
files = glob.glob(src)
|
||||||
if not files:
|
if not files:
|
||||||
raise IOError("No such file or directory: '{0}'".format(src))
|
raise OSError("No such file or directory: '{0}'".format(src))
|
||||||
|
|
||||||
# For Windows hard-links and junctions, the source path must exist to make a symlink. Add
|
# For Windows hard-links and junctions, the source path must exist to make a symlink. Add
|
||||||
# all symlinks to this list while traversing the tree, then when finished, make all
|
# all symlinks to this list while traversing the tree, then when finished, make all
|
||||||
@@ -844,7 +843,7 @@ def install_tree(src, dest, symlinks=True, ignore=None):
|
|||||||
ignore (typing.Callable): function indicating which files to ignore
|
ignore (typing.Callable): function indicating which files to ignore
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
IOError: if *src* does not match any files or directories
|
OSError: if *src* does not match any files or directories
|
||||||
ValueError: if *src* is a parent directory of *dest*
|
ValueError: if *src* is a parent directory of *dest*
|
||||||
"""
|
"""
|
||||||
copy_tree(src, dest, symlinks=symlinks, ignore=ignore, _permissions=True)
|
copy_tree(src, dest, symlinks=symlinks, ignore=ignore, _permissions=True)
|
||||||
@@ -1470,15 +1469,36 @@ def set_executable(path):
|
|||||||
|
|
||||||
|
|
||||||
@system_path_filter
|
@system_path_filter
|
||||||
def last_modification_time_recursive(path):
|
def recursive_mtime_greater_than(path: str, time: float) -> bool:
|
||||||
path = os.path.abspath(path)
|
"""Returns true if any file or dir recursively under `path` has mtime greater than `time`."""
|
||||||
times = [os.stat(path).st_mtime]
|
# use bfs order to increase likelihood of early return
|
||||||
times.extend(
|
queue: Deque[str] = collections.deque([path])
|
||||||
os.lstat(os.path.join(root, name)).st_mtime
|
|
||||||
for root, dirs, files in os.walk(path)
|
if os.stat(path).st_mtime > time:
|
||||||
for name in dirs + files
|
return True
|
||||||
)
|
|
||||||
return max(times)
|
while queue:
|
||||||
|
current = queue.popleft()
|
||||||
|
|
||||||
|
try:
|
||||||
|
entries = os.scandir(current)
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
with entries:
|
||||||
|
for entry in entries:
|
||||||
|
try:
|
||||||
|
st = entry.stat(follow_symlinks=False)
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if st.st_mtime > time:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if entry.is_dir(follow_symlinks=False):
|
||||||
|
queue.append(entry.path)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@system_path_filter
|
@system_path_filter
|
||||||
@@ -1740,8 +1760,7 @@ def find(
|
|||||||
|
|
||||||
|
|
||||||
def _log_file_access_issue(e: OSError, path: str) -> None:
|
def _log_file_access_issue(e: OSError, path: str) -> None:
|
||||||
errno_name = errno.errorcode.get(e.errno, "UNKNOWN")
|
tty.debug(f"find must skip {path}: {e}")
|
||||||
tty.debug(f"find must skip {path}: {errno_name} {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def _file_id(s: os.stat_result) -> Tuple[int, int]:
|
def _file_id(s: os.stat_result) -> Tuple[int, int]:
|
||||||
|
@@ -308,7 +308,7 @@ class LinkTree:
|
|||||||
|
|
||||||
def __init__(self, source_root):
|
def __init__(self, source_root):
|
||||||
if not os.path.exists(source_root):
|
if not os.path.exists(source_root):
|
||||||
raise IOError("No such file or directory: '%s'", source_root)
|
raise OSError("No such file or directory: '%s'", source_root)
|
||||||
|
|
||||||
self._root = source_root
|
self._root = source_root
|
||||||
|
|
||||||
|
@@ -391,7 +391,7 @@ def _poll_lock(self, op: int) -> bool:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except IOError as e:
|
except OSError as e:
|
||||||
# EAGAIN and EACCES == locked by another process (so try again)
|
# EAGAIN and EACCES == locked by another process (so try again)
|
||||||
if e.errno not in (errno.EAGAIN, errno.EACCES):
|
if e.errno not in (errno.EAGAIN, errno.EACCES):
|
||||||
raise
|
raise
|
||||||
|
@@ -344,26 +344,6 @@ def close(self):
|
|||||||
self.file.close()
|
self.file.close()
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def replace_environment(env):
|
|
||||||
"""Replace the current environment (`os.environ`) with `env`.
|
|
||||||
|
|
||||||
If `env` is empty (or None), this unsets all current environment
|
|
||||||
variables.
|
|
||||||
"""
|
|
||||||
env = env or {}
|
|
||||||
old_env = os.environ.copy()
|
|
||||||
try:
|
|
||||||
os.environ.clear()
|
|
||||||
for name, val in env.items():
|
|
||||||
os.environ[name] = val
|
|
||||||
yield
|
|
||||||
finally:
|
|
||||||
os.environ.clear()
|
|
||||||
for name, val in old_env.items():
|
|
||||||
os.environ[name] = val
|
|
||||||
|
|
||||||
|
|
||||||
def log_output(*args, **kwargs):
|
def log_output(*args, **kwargs):
|
||||||
"""Context manager that logs its output to a file.
|
"""Context manager that logs its output to a file.
|
||||||
|
|
||||||
@@ -447,7 +427,6 @@ def __init__(
|
|||||||
self.echo = echo
|
self.echo = echo
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.buffer = buffer
|
self.buffer = buffer
|
||||||
self.env = env # the environment to use for _writer_daemon
|
|
||||||
self.filter_fn = filter_fn
|
self.filter_fn = filter_fn
|
||||||
|
|
||||||
self._active = False # used to prevent re-entry
|
self._active = False # used to prevent re-entry
|
||||||
@@ -519,21 +498,20 @@ def __enter__(self):
|
|||||||
# just don't forward input if this fails
|
# just don't forward input if this fails
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with replace_environment(self.env):
|
self.process = multiprocessing.Process(
|
||||||
self.process = multiprocessing.Process(
|
target=_writer_daemon,
|
||||||
target=_writer_daemon,
|
args=(
|
||||||
args=(
|
input_fd,
|
||||||
input_fd,
|
read_fd,
|
||||||
read_fd,
|
self.write_fd,
|
||||||
self.write_fd,
|
self.echo,
|
||||||
self.echo,
|
self.log_file,
|
||||||
self.log_file,
|
child_pipe,
|
||||||
child_pipe,
|
self.filter_fn,
|
||||||
self.filter_fn,
|
),
|
||||||
),
|
)
|
||||||
)
|
self.process.daemon = True # must set before start()
|
||||||
self.process.daemon = True # must set before start()
|
self.process.start()
|
||||||
self.process.start()
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if input_fd:
|
if input_fd:
|
||||||
@@ -729,10 +707,7 @@ class winlog:
|
|||||||
Does not support the use of 'v' toggling as nixlog does.
|
Does not support the use of 'v' toggling as nixlog does.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, file_like=None, echo=False, debug=0, buffer=False, filter_fn=None):
|
||||||
self, file_like=None, echo=False, debug=0, buffer=False, env=None, filter_fn=None
|
|
||||||
):
|
|
||||||
self.env = env
|
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.echo = echo
|
self.echo = echo
|
||||||
self.logfile = file_like
|
self.logfile = file_like
|
||||||
@@ -789,11 +764,10 @@ def background_reader(reader, echo_writer, _kill):
|
|||||||
reader.close()
|
reader.close()
|
||||||
|
|
||||||
self._active = True
|
self._active = True
|
||||||
with replace_environment(self.env):
|
self._thread = Thread(
|
||||||
self._thread = Thread(
|
target=background_reader, args=(self.reader, self.echo_writer, self._kill)
|
||||||
target=background_reader, args=(self.reader, self.echo_writer, self._kill)
|
)
|
||||||
)
|
self._thread.start()
|
||||||
self._thread.start()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
@@ -918,7 +892,7 @@ def _writer_daemon(
|
|||||||
try:
|
try:
|
||||||
if stdin_file.read(1) == "v":
|
if stdin_file.read(1) == "v":
|
||||||
echo = not echo
|
echo = not echo
|
||||||
except IOError as e:
|
except OSError as e:
|
||||||
# If SIGTTIN is ignored, the system gives EIO
|
# If SIGTTIN is ignored, the system gives EIO
|
||||||
# to let the caller know the read failed b/c it
|
# to let the caller know the read failed b/c it
|
||||||
# was in the bg. Ignore that too.
|
# was in the bg. Ignore that too.
|
||||||
@@ -1013,7 +987,7 @@ def wrapped(*args, **kwargs):
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
return function(*args, **kwargs)
|
return function(*args, **kwargs)
|
||||||
except IOError as e:
|
except OSError as e:
|
||||||
if e.errno == errno.EINTR:
|
if e.errno == errno.EINTR:
|
||||||
continue
|
continue
|
||||||
raise
|
raise
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
import spack.util.git
|
import spack.util.git
|
||||||
|
|
||||||
#: PEP440 canonical <major>.<minor>.<micro>.<devN> string
|
#: PEP440 canonical <major>.<minor>.<micro>.<devN> string
|
||||||
__version__ = "0.24.0.dev0"
|
__version__ = "1.0.0.dev0"
|
||||||
spack_version = __version__
|
spack_version = __version__
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1010,7 +1010,7 @@ def _issues_in_depends_on_directive(pkgs, error_cls):
|
|||||||
for dep_name, dep in deps_by_name.items():
|
for dep_name, dep in deps_by_name.items():
|
||||||
|
|
||||||
def check_virtual_with_variants(spec, msg):
|
def check_virtual_with_variants(spec, msg):
|
||||||
if not spec.virtual or not spec.variants:
|
if not spack.repo.PATH.is_virtual(spec.name) or not spec.variants:
|
||||||
return
|
return
|
||||||
error = error_cls(
|
error = error_cls(
|
||||||
f"{pkg_name}: {msg}",
|
f"{pkg_name}: {msg}",
|
||||||
@@ -1356,14 +1356,8 @@ def _test_detection_by_executable(pkgs, debug_log, error_cls):
|
|||||||
|
|
||||||
def _compare_extra_attribute(_expected, _detected, *, _spec):
|
def _compare_extra_attribute(_expected, _detected, *, _spec):
|
||||||
result = []
|
result = []
|
||||||
# Check items are of the same type
|
|
||||||
if not isinstance(_detected, type(_expected)):
|
|
||||||
_summary = f'{pkg_name}: error when trying to detect "{_expected}"'
|
|
||||||
_details = [f"{_detected} was detected instead"]
|
|
||||||
return [error_cls(summary=_summary, details=_details)]
|
|
||||||
|
|
||||||
# If they are string expected is a regex
|
# If they are string expected is a regex
|
||||||
if isinstance(_expected, str):
|
if isinstance(_expected, str) and isinstance(_detected, str):
|
||||||
try:
|
try:
|
||||||
_regex = re.compile(_expected)
|
_regex = re.compile(_expected)
|
||||||
except re.error:
|
except re.error:
|
||||||
@@ -1379,7 +1373,7 @@ def _compare_extra_attribute(_expected, _detected, *, _spec):
|
|||||||
_details = [f"{_detected} does not match the regex"]
|
_details = [f"{_detected} does not match the regex"]
|
||||||
return [error_cls(summary=_summary, details=_details)]
|
return [error_cls(summary=_summary, details=_details)]
|
||||||
|
|
||||||
if isinstance(_expected, dict):
|
elif isinstance(_expected, dict) and isinstance(_detected, dict):
|
||||||
_not_detected = set(_expected.keys()) - set(_detected.keys())
|
_not_detected = set(_expected.keys()) - set(_detected.keys())
|
||||||
if _not_detected:
|
if _not_detected:
|
||||||
_summary = f"{pkg_name}: cannot detect some attributes for spec {_spec}"
|
_summary = f"{pkg_name}: cannot detect some attributes for spec {_spec}"
|
||||||
@@ -1394,6 +1388,10 @@ def _compare_extra_attribute(_expected, _detected, *, _spec):
|
|||||||
result.extend(
|
result.extend(
|
||||||
_compare_extra_attribute(_expected[_key], _detected[_key], _spec=_spec)
|
_compare_extra_attribute(_expected[_key], _detected[_key], _spec=_spec)
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
_summary = f'{pkg_name}: error when trying to detect "{_expected}"'
|
||||||
|
_details = [f"{_detected} was detected instead"]
|
||||||
|
return [error_cls(summary=_summary, details=_details)]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@@ -5,6 +5,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
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
import warnings
|
import warnings
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from typing import IO, Dict, Iterable, List, NamedTuple, Optional, Set, Tuple, Union
|
from typing import IO, Callable, Dict, 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
|
||||||
@@ -91,6 +92,9 @@
|
|||||||
CURRENT_BUILD_CACHE_LAYOUT_VERSION = 2
|
CURRENT_BUILD_CACHE_LAYOUT_VERSION = 2
|
||||||
|
|
||||||
|
|
||||||
|
INDEX_HASH_FILE = "index.json.hash"
|
||||||
|
|
||||||
|
|
||||||
class BuildCacheDatabase(spack_db.Database):
|
class BuildCacheDatabase(spack_db.Database):
|
||||||
"""A database for binary buildcaches.
|
"""A database for binary buildcaches.
|
||||||
|
|
||||||
@@ -502,7 +506,7 @@ def _fetch_and_cache_index(self, mirror_url, cache_entry={}):
|
|||||||
scheme = urllib.parse.urlparse(mirror_url).scheme
|
scheme = urllib.parse.urlparse(mirror_url).scheme
|
||||||
|
|
||||||
if scheme != "oci" and not web_util.url_exists(
|
if scheme != "oci" and not web_util.url_exists(
|
||||||
url_util.join(mirror_url, BUILD_CACHE_RELATIVE_PATH, "index.json")
|
url_util.join(mirror_url, BUILD_CACHE_RELATIVE_PATH, spack_db.INDEX_JSON_FILE)
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -669,19 +673,24 @@ def sign_specfile(key: str, specfile_path: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _read_specs_and_push_index(
|
def _read_specs_and_push_index(
|
||||||
file_list, read_method, cache_prefix, db: BuildCacheDatabase, temp_dir, concurrency
|
file_list: List[str],
|
||||||
|
read_method: Callable,
|
||||||
|
cache_prefix: str,
|
||||||
|
db: BuildCacheDatabase,
|
||||||
|
temp_dir: str,
|
||||||
|
concurrency: int,
|
||||||
):
|
):
|
||||||
"""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.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
file_list (list(str)): List of urls or file paths pointing at spec files to read
|
file_list: List of urls or file paths pointing at spec files to read
|
||||||
read_method: A function taking a single argument, either a url or a file path,
|
read_method: A function taking a single argument, either a url or a file path,
|
||||||
and which reads the spec file at that location, and returns the spec.
|
and which reads the spec file at that location, and returns the spec.
|
||||||
cache_prefix (str): prefix of the build cache on s3 where index should be pushed.
|
cache_prefix: prefix of the build cache on s3 where index should be pushed.
|
||||||
db: A spack database used for adding specs and then writing the index.
|
db: A spack database used for adding specs and then writing the index.
|
||||||
temp_dir (str): Location to write index.json and hash for pushing
|
temp_dir: Location to write index.json and hash for pushing
|
||||||
concurrency (int): Number of parallel processes to use when fetching
|
concurrency: Number of parallel processes to use when fetching
|
||||||
"""
|
"""
|
||||||
for file in file_list:
|
for file in file_list:
|
||||||
contents = read_method(file)
|
contents = read_method(file)
|
||||||
@@ -699,7 +708,7 @@ def _read_specs_and_push_index(
|
|||||||
|
|
||||||
# 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
|
||||||
# the mirror.
|
# the mirror.
|
||||||
index_json_path = os.path.join(temp_dir, "index.json")
|
index_json_path = os.path.join(temp_dir, spack_db.INDEX_JSON_FILE)
|
||||||
with open(index_json_path, "w", encoding="utf-8") as f:
|
with open(index_json_path, "w", encoding="utf-8") as f:
|
||||||
db._write_to_file(f)
|
db._write_to_file(f)
|
||||||
|
|
||||||
@@ -709,14 +718,14 @@ def _read_specs_and_push_index(
|
|||||||
index_hash = compute_hash(index_string)
|
index_hash = compute_hash(index_string)
|
||||||
|
|
||||||
# Write the hash out to a local file
|
# Write the hash out to a local file
|
||||||
index_hash_path = os.path.join(temp_dir, "index.json.hash")
|
index_hash_path = os.path.join(temp_dir, INDEX_HASH_FILE)
|
||||||
with open(index_hash_path, "w", encoding="utf-8") as f:
|
with open(index_hash_path, "w", encoding="utf-8") as f:
|
||||||
f.write(index_hash)
|
f.write(index_hash)
|
||||||
|
|
||||||
# Push the index itself
|
# Push the index itself
|
||||||
web_util.push_to_url(
|
web_util.push_to_url(
|
||||||
index_json_path,
|
index_json_path,
|
||||||
url_util.join(cache_prefix, "index.json"),
|
url_util.join(cache_prefix, spack_db.INDEX_JSON_FILE),
|
||||||
keep_original=False,
|
keep_original=False,
|
||||||
extra_args={"ContentType": "application/json", "CacheControl": "no-cache"},
|
extra_args={"ContentType": "application/json", "CacheControl": "no-cache"},
|
||||||
)
|
)
|
||||||
@@ -724,7 +733,7 @@ def _read_specs_and_push_index(
|
|||||||
# Push the hash
|
# Push the hash
|
||||||
web_util.push_to_url(
|
web_util.push_to_url(
|
||||||
index_hash_path,
|
index_hash_path,
|
||||||
url_util.join(cache_prefix, "index.json.hash"),
|
url_util.join(cache_prefix, INDEX_HASH_FILE),
|
||||||
keep_original=False,
|
keep_original=False,
|
||||||
extra_args={"ContentType": "text/plain", "CacheControl": "no-cache"},
|
extra_args={"ContentType": "text/plain", "CacheControl": "no-cache"},
|
||||||
)
|
)
|
||||||
@@ -793,7 +802,7 @@ def url_read_method(url):
|
|||||||
try:
|
try:
|
||||||
_, _, spec_file = web_util.read_from_url(url)
|
_, _, spec_file = web_util.read_from_url(url)
|
||||||
contents = codecs.getreader("utf-8")(spec_file).read()
|
contents = codecs.getreader("utf-8")(spec_file).read()
|
||||||
except web_util.SpackWebError as e:
|
except (web_util.SpackWebError, OSError) as e:
|
||||||
tty.error(f"Error reading specfile: {url}: {e}")
|
tty.error(f"Error reading specfile: {url}: {e}")
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
@@ -861,9 +870,12 @@ def _url_generate_package_index(url: str, tmpdir: str, concurrency: int = 32):
|
|||||||
tty.debug(f"Retrieving spec descriptor files from {url} to build index")
|
tty.debug(f"Retrieving spec descriptor files from {url} to build index")
|
||||||
|
|
||||||
db = BuildCacheDatabase(tmpdir)
|
db = BuildCacheDatabase(tmpdir)
|
||||||
|
db._write()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_read_specs_and_push_index(file_list, read_fn, url, db, db.database_directory, concurrency)
|
_read_specs_and_push_index(
|
||||||
|
file_list, read_fn, url, db, str(db.database_directory), concurrency
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise GenerateIndexError(f"Encountered problem pushing package index to {url}: {e}") from e
|
raise GenerateIndexError(f"Encountered problem pushing package index to {url}: {e}") from e
|
||||||
|
|
||||||
@@ -1777,7 +1789,7 @@ def _oci_update_index(
|
|||||||
db.mark(spec, "in_buildcache", True)
|
db.mark(spec, "in_buildcache", True)
|
||||||
|
|
||||||
# Create the index.json file
|
# Create the index.json file
|
||||||
index_json_path = os.path.join(tmpdir, "index.json")
|
index_json_path = os.path.join(tmpdir, spack_db.INDEX_JSON_FILE)
|
||||||
with open(index_json_path, "w", encoding="utf-8") as f:
|
with open(index_json_path, "w", encoding="utf-8") as f:
|
||||||
db._write_to_file(f)
|
db._write_to_file(f)
|
||||||
|
|
||||||
@@ -1998,7 +2010,7 @@ def fetch_url_to_mirror(url):
|
|||||||
|
|
||||||
# Download the config = spec.json and the relevant tarball
|
# Download the config = spec.json and the relevant tarball
|
||||||
try:
|
try:
|
||||||
manifest = json.loads(response.read())
|
manifest = json.load(response)
|
||||||
spec_digest = spack.oci.image.Digest.from_string(manifest["config"]["digest"])
|
spec_digest = spack.oci.image.Digest.from_string(manifest["config"]["digest"])
|
||||||
tarball_digest = spack.oci.image.Digest.from_string(
|
tarball_digest = spack.oci.image.Digest.from_string(
|
||||||
manifest["layers"][-1]["digest"]
|
manifest["layers"][-1]["digest"]
|
||||||
@@ -2125,10 +2137,9 @@ def fetch_url_to_mirror(url):
|
|||||||
|
|
||||||
|
|
||||||
def dedupe_hardlinks_if_necessary(root, buildinfo):
|
def dedupe_hardlinks_if_necessary(root, buildinfo):
|
||||||
"""Updates a buildinfo dict for old archives that did
|
"""Updates a buildinfo dict for old archives that did not dedupe hardlinks. De-duping hardlinks
|
||||||
not dedupe hardlinks. De-duping hardlinks is necessary
|
is necessary when relocating files in parallel and in-place. This means we must preserve inodes
|
||||||
when relocating files in parallel and in-place. This
|
when relocating."""
|
||||||
means we must preserve inodes when relocating."""
|
|
||||||
|
|
||||||
# New archives don't need this.
|
# New archives don't need this.
|
||||||
if buildinfo.get("hardlinks_deduped", False):
|
if buildinfo.get("hardlinks_deduped", False):
|
||||||
@@ -2157,69 +2168,47 @@ def dedupe_hardlinks_if_necessary(root, buildinfo):
|
|||||||
buildinfo[key] = new_list
|
buildinfo[key] = new_list
|
||||||
|
|
||||||
|
|
||||||
def relocate_package(spec):
|
def relocate_package(spec: spack.spec.Spec) -> None:
|
||||||
"""
|
"""Relocate binaries and text files in the given spec prefix, based on its buildinfo file."""
|
||||||
Relocate the given package
|
spec_prefix = str(spec.prefix)
|
||||||
"""
|
buildinfo = read_buildinfo_file(spec_prefix)
|
||||||
workdir = str(spec.prefix)
|
|
||||||
buildinfo = read_buildinfo_file(workdir)
|
|
||||||
new_layout_root = str(spack.store.STORE.layout.root)
|
|
||||||
new_prefix = str(spec.prefix)
|
|
||||||
new_rel_prefix = str(os.path.relpath(new_prefix, new_layout_root))
|
|
||||||
new_spack_prefix = str(spack.paths.prefix)
|
|
||||||
|
|
||||||
old_sbang_install_path = None
|
|
||||||
if "sbang_install_path" in buildinfo:
|
|
||||||
old_sbang_install_path = str(buildinfo["sbang_install_path"])
|
|
||||||
old_layout_root = str(buildinfo["buildpath"])
|
old_layout_root = str(buildinfo["buildpath"])
|
||||||
old_spack_prefix = str(buildinfo.get("spackprefix"))
|
|
||||||
old_rel_prefix = buildinfo.get("relative_prefix")
|
|
||||||
old_prefix = os.path.join(old_layout_root, old_rel_prefix)
|
|
||||||
|
|
||||||
# Warn about old style tarballs created with the now removed --rel flag.
|
# Warn about old style tarballs created with the --rel flag (removed in Spack v0.20)
|
||||||
if buildinfo.get("relative_rpaths", False):
|
if buildinfo.get("relative_rpaths", False):
|
||||||
tty.warn(
|
tty.warn(
|
||||||
f"Tarball for {spec} uses relative rpaths, " "which can cause library loading issues."
|
f"Tarball for {spec} uses relative rpaths, which can cause library loading issues."
|
||||||
)
|
)
|
||||||
|
|
||||||
# In the past prefix_to_hash was the default and externals were not dropped, so prefixes
|
# In Spack 0.19 and older prefix_to_hash was the default and externals were not dropped, so
|
||||||
# were not unique.
|
# prefixes were not unique.
|
||||||
if "hash_to_prefix" in buildinfo:
|
if "hash_to_prefix" in buildinfo:
|
||||||
hash_to_old_prefix = buildinfo["hash_to_prefix"]
|
hash_to_old_prefix = buildinfo["hash_to_prefix"]
|
||||||
elif "prefix_to_hash" in buildinfo:
|
elif "prefix_to_hash" in buildinfo:
|
||||||
hash_to_old_prefix = dict((v, k) for (k, v) in buildinfo["prefix_to_hash"].items())
|
hash_to_old_prefix = {v: k for (k, v) in buildinfo["prefix_to_hash"].items()}
|
||||||
else:
|
else:
|
||||||
hash_to_old_prefix = dict()
|
raise NewLayoutException(
|
||||||
|
"Package tarball was created from an install prefix with a different directory layout "
|
||||||
|
"and an older buildcache create implementation. It cannot be relocated."
|
||||||
|
)
|
||||||
|
|
||||||
if old_rel_prefix != new_rel_prefix and not hash_to_old_prefix:
|
prefix_to_prefix: Dict[str, str] = {}
|
||||||
msg = "Package tarball was created from an install "
|
|
||||||
msg += "prefix with a different directory layout and an older "
|
|
||||||
msg += "buildcache create implementation. It cannot be relocated."
|
|
||||||
raise NewLayoutException(msg)
|
|
||||||
|
|
||||||
# Spurious replacements (e.g. sbang) will cause issues with binaries
|
if "sbang_install_path" in buildinfo:
|
||||||
# For example, the new sbang can be longer than the old one.
|
old_sbang_install_path = str(buildinfo["sbang_install_path"])
|
||||||
# Hence 2 dictionaries are maintained here.
|
prefix_to_prefix[old_sbang_install_path] = spack.hooks.sbang.sbang_install_path()
|
||||||
prefix_to_prefix_text = collections.OrderedDict()
|
|
||||||
prefix_to_prefix_bin = collections.OrderedDict()
|
|
||||||
|
|
||||||
if old_sbang_install_path:
|
# First match specific prefix paths. Possibly the *local* install prefix of some dependency is
|
||||||
install_path = spack.hooks.sbang.sbang_install_path()
|
# in an upstream, so we cannot assume the original spack store root can be mapped uniformly to
|
||||||
prefix_to_prefix_text[old_sbang_install_path] = install_path
|
# the new spack store root.
|
||||||
|
|
||||||
# First match specific prefix paths. Possibly the *local* install prefix
|
# If the spec is spliced, we need to handle the simultaneous mapping from the old install_tree
|
||||||
# of some dependency is in an upstream, so we cannot assume the original
|
# to the new install_tree and from the build_spec to the spliced spec. Because foo.build_spec
|
||||||
# spack store root can be mapped uniformly to the new spack store root.
|
# is foo for any non-spliced spec, we can simplify by checking for spliced-in nodes by checking
|
||||||
#
|
# for nodes not in the build_spec without any explicit check for whether the spec is spliced.
|
||||||
# If the spec is spliced, we need to handle the simultaneous mapping
|
# An analog in this algorithm is any spec that shares a name or provides the same virtuals in
|
||||||
# from the old install_tree to the new install_tree and from the build_spec
|
# the context of the relevant root spec. This ensures that the analog for a spec s is the spec
|
||||||
# to the spliced spec.
|
# that s replaced when we spliced.
|
||||||
# Because foo.build_spec is foo for any non-spliced spec, we can simplify
|
|
||||||
# by checking for spliced-in nodes by checking for nodes not in the build_spec
|
|
||||||
# without any explicit check for whether the spec is spliced.
|
|
||||||
# An analog in this algorithm is any spec that shares a name or provides the same virtuals
|
|
||||||
# in the context of the relevant root spec. This ensures that the analog for a spec s
|
|
||||||
# is the spec that s replaced when we spliced.
|
|
||||||
relocation_specs = specs_to_relocate(spec)
|
relocation_specs = specs_to_relocate(spec)
|
||||||
build_spec_ids = set(id(s) for s in spec.build_spec.traverse(deptype=dt.ALL & ~dt.BUILD))
|
build_spec_ids = set(id(s) for s in spec.build_spec.traverse(deptype=dt.ALL & ~dt.BUILD))
|
||||||
for s in relocation_specs:
|
for s in relocation_specs:
|
||||||
@@ -2239,72 +2228,66 @@ def relocate_package(spec):
|
|||||||
lookup_dag_hash = analog.dag_hash()
|
lookup_dag_hash = analog.dag_hash()
|
||||||
if lookup_dag_hash in hash_to_old_prefix:
|
if lookup_dag_hash in hash_to_old_prefix:
|
||||||
old_dep_prefix = hash_to_old_prefix[lookup_dag_hash]
|
old_dep_prefix = hash_to_old_prefix[lookup_dag_hash]
|
||||||
prefix_to_prefix_bin[old_dep_prefix] = str(s.prefix)
|
prefix_to_prefix[old_dep_prefix] = str(s.prefix)
|
||||||
prefix_to_prefix_text[old_dep_prefix] = str(s.prefix)
|
|
||||||
|
|
||||||
# Only then add the generic fallback of install prefix -> install prefix.
|
# Only then add the generic fallback of install prefix -> install prefix.
|
||||||
prefix_to_prefix_text[old_prefix] = new_prefix
|
prefix_to_prefix[old_layout_root] = str(spack.store.STORE.layout.root)
|
||||||
prefix_to_prefix_bin[old_prefix] = new_prefix
|
|
||||||
prefix_to_prefix_text[old_layout_root] = new_layout_root
|
|
||||||
prefix_to_prefix_bin[old_layout_root] = new_layout_root
|
|
||||||
|
|
||||||
# This is vestigial code for the *old* location of sbang. Previously,
|
# Delete identity mappings from prefix_to_prefix
|
||||||
# sbang was a bash script, and it lived in the spack prefix. It is
|
prefix_to_prefix = {k: v for k, v in prefix_to_prefix.items() if k != v}
|
||||||
# now a POSIX script that lives in the install prefix. Old packages
|
|
||||||
# will have the old sbang location in their shebangs.
|
|
||||||
orig_sbang = "#!/bin/bash {0}/bin/sbang".format(old_spack_prefix)
|
|
||||||
new_sbang = spack.hooks.sbang.sbang_shebang_line()
|
|
||||||
prefix_to_prefix_text[orig_sbang] = new_sbang
|
|
||||||
|
|
||||||
tty.debug("Relocating package from", "%s to %s." % (old_layout_root, new_layout_root))
|
# If there's nothing to relocate, we're done.
|
||||||
|
if not prefix_to_prefix:
|
||||||
|
return
|
||||||
|
|
||||||
|
for old, new in prefix_to_prefix.items():
|
||||||
|
tty.debug(f"Relocating: {old} => {new}.")
|
||||||
|
|
||||||
# Old archives may have hardlinks repeated.
|
# Old archives may have hardlinks repeated.
|
||||||
dedupe_hardlinks_if_necessary(workdir, buildinfo)
|
dedupe_hardlinks_if_necessary(spec_prefix, buildinfo)
|
||||||
|
|
||||||
# Text files containing the prefix text
|
# Text files containing the prefix text
|
||||||
text_names = [os.path.join(workdir, f) for f in buildinfo["relocate_textfiles"]]
|
textfiles = [os.path.join(spec_prefix, f) for f in buildinfo["relocate_textfiles"]]
|
||||||
|
binaries = [os.path.join(spec_prefix, f) for f in buildinfo.get("relocate_binaries")]
|
||||||
|
links = [os.path.join(spec_prefix, f) for f in buildinfo.get("relocate_links", [])]
|
||||||
|
|
||||||
# If we are not installing back to the same install tree do the relocation
|
platform = spack.platforms.by_name(spec.platform)
|
||||||
if old_prefix != new_prefix:
|
if "macho" in platform.binary_formats:
|
||||||
files_to_relocate = [
|
relocate.relocate_macho_binaries(binaries, prefix_to_prefix)
|
||||||
os.path.join(workdir, filename) for filename in buildinfo.get("relocate_binaries")
|
elif "elf" in platform.binary_formats:
|
||||||
]
|
relocate.relocate_elf_binaries(binaries, prefix_to_prefix)
|
||||||
# If the buildcache was not created with relativized rpaths
|
|
||||||
# do the relocation of path in binaries
|
|
||||||
platform = spack.platforms.by_name(spec.platform)
|
|
||||||
if "macho" in platform.binary_formats:
|
|
||||||
relocate.relocate_macho_binaries(files_to_relocate, prefix_to_prefix_bin)
|
|
||||||
elif "elf" in platform.binary_formats:
|
|
||||||
# The new ELF dynamic section relocation logic only handles absolute to
|
|
||||||
# absolute relocation.
|
|
||||||
relocate.relocate_elf_binaries(files_to_relocate, prefix_to_prefix_bin)
|
|
||||||
|
|
||||||
# Relocate links to the new install prefix
|
relocate.relocate_links(links, prefix_to_prefix)
|
||||||
links = [os.path.join(workdir, f) for f in buildinfo.get("relocate_links", [])]
|
relocate.relocate_text(textfiles, prefix_to_prefix)
|
||||||
relocate.relocate_links(links, prefix_to_prefix_bin)
|
changed_files = relocate.relocate_text_bin(binaries, prefix_to_prefix)
|
||||||
|
|
||||||
# For all buildcaches
|
# Add ad-hoc signatures to patched macho files when on macOS.
|
||||||
# relocate the install prefixes in text files including dependencies
|
if "macho" in platform.binary_formats and sys.platform == "darwin":
|
||||||
relocate.relocate_text(text_names, prefix_to_prefix_text)
|
codesign = which("codesign")
|
||||||
|
if not codesign:
|
||||||
|
return
|
||||||
|
for binary in changed_files:
|
||||||
|
# preserve the original inode by running codesign on a copy
|
||||||
|
with fsys.edit_in_place_through_temporary_file(binary) as tmp_binary:
|
||||||
|
codesign("-fs-", tmp_binary)
|
||||||
|
|
||||||
# relocate the install prefixes in binary files including dependencies
|
install_manifest = os.path.join(
|
||||||
changed_files = relocate.relocate_text_bin(files_to_relocate, prefix_to_prefix_bin)
|
spec.prefix,
|
||||||
|
spack.store.STORE.layout.metadata_dir,
|
||||||
|
spack.store.STORE.layout.manifest_file_name,
|
||||||
|
)
|
||||||
|
if not os.path.exists(install_manifest):
|
||||||
|
spec_id = spec.format("{name}/{hash:7}")
|
||||||
|
tty.warn("No manifest file in tarball for spec %s" % spec_id)
|
||||||
|
|
||||||
# Add ad-hoc signatures to patched macho files when on macOS.
|
# overwrite old metadata with new
|
||||||
if "macho" in platform.binary_formats and sys.platform == "darwin":
|
if spec.spliced:
|
||||||
codesign = which("codesign")
|
# rewrite spec on disk
|
||||||
if not codesign:
|
spack.store.STORE.layout.write_spec(spec, spack.store.STORE.layout.spec_file_path(spec))
|
||||||
return
|
|
||||||
for binary in changed_files:
|
|
||||||
# preserve the original inode by running codesign on a copy
|
|
||||||
with fsys.edit_in_place_through_temporary_file(binary) as tmp_binary:
|
|
||||||
codesign("-fs-", tmp_binary)
|
|
||||||
|
|
||||||
# If we are installing back to the same location
|
# de-cache the install manifest
|
||||||
# relocate the sbang location if the spack directory changed
|
with contextlib.suppress(FileNotFoundError):
|
||||||
else:
|
os.unlink(install_manifest)
|
||||||
if old_spack_prefix != new_spack_prefix:
|
|
||||||
relocate.relocate_text(text_names, prefix_to_prefix_text)
|
|
||||||
|
|
||||||
|
|
||||||
def _extract_inner_tarball(spec, filename, extract_to, signature_required: bool, remote_checksum):
|
def _extract_inner_tarball(spec, filename, extract_to, signature_required: bool, remote_checksum):
|
||||||
@@ -2472,15 +2455,6 @@ def extract_tarball(spec, download_result, force=False, timer=timer.NULL_TIMER):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
shutil.rmtree(spec.prefix, ignore_errors=True)
|
shutil.rmtree(spec.prefix, ignore_errors=True)
|
||||||
raise e
|
raise e
|
||||||
else:
|
|
||||||
manifest_file = os.path.join(
|
|
||||||
spec.prefix,
|
|
||||||
spack.store.STORE.layout.metadata_dir,
|
|
||||||
spack.store.STORE.layout.manifest_file_name,
|
|
||||||
)
|
|
||||||
if not os.path.exists(manifest_file):
|
|
||||||
spec_id = spec.format("{name}/{hash:7}")
|
|
||||||
tty.warn("No manifest file in tarball for spec %s" % spec_id)
|
|
||||||
finally:
|
finally:
|
||||||
if tmpdir:
|
if tmpdir:
|
||||||
shutil.rmtree(tmpdir, ignore_errors=True)
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||||
@@ -2555,10 +2529,10 @@ def install_root_node(
|
|||||||
allow_missing: when true, allows installing a node with missing dependencies
|
allow_missing: when true, allows installing a node with missing dependencies
|
||||||
"""
|
"""
|
||||||
# Early termination
|
# Early termination
|
||||||
if spec.external or spec.virtual:
|
if spec.external or not spec.concrete:
|
||||||
warnings.warn("Skipping external or virtual package {0}".format(spec.format()))
|
warnings.warn("Skipping external or abstract spec {0}".format(spec.format()))
|
||||||
return
|
return
|
||||||
elif spec.concrete and spec.installed and not force:
|
elif spec.installed and not force:
|
||||||
warnings.warn("Package for spec {0} already installed.".format(spec.format()))
|
warnings.warn("Package for spec {0} already installed.".format(spec.format()))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -2585,10 +2559,6 @@ def install_root_node(
|
|||||||
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()
|
spec.package.windows_establish_runtime_linkage()
|
||||||
if spec.spliced: # overwrite old metadata with new
|
|
||||||
spack.store.STORE.layout.write_spec(
|
|
||||||
spec, spack.store.STORE.layout.spec_file_path(spec)
|
|
||||||
)
|
|
||||||
spack.hooks.post_install(spec, False)
|
spack.hooks.post_install(spec, False)
|
||||||
spack.store.STORE.db.add(spec, allow_missing=allow_missing)
|
spack.store.STORE.db.add(spec, allow_missing=allow_missing)
|
||||||
|
|
||||||
@@ -2626,11 +2596,14 @@ def try_direct_fetch(spec, mirrors=None):
|
|||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
_, _, fs = web_util.read_from_url(buildcache_fetch_url_signed_json)
|
_, _, fs = web_util.read_from_url(buildcache_fetch_url_signed_json)
|
||||||
|
specfile_contents = codecs.getreader("utf-8")(fs).read()
|
||||||
specfile_is_signed = True
|
specfile_is_signed = True
|
||||||
except web_util.SpackWebError as e1:
|
except (web_util.SpackWebError, OSError) as e1:
|
||||||
try:
|
try:
|
||||||
_, _, fs = web_util.read_from_url(buildcache_fetch_url_json)
|
_, _, fs = web_util.read_from_url(buildcache_fetch_url_json)
|
||||||
except web_util.SpackWebError as e2:
|
specfile_contents = codecs.getreader("utf-8")(fs).read()
|
||||||
|
specfile_is_signed = False
|
||||||
|
except (web_util.SpackWebError, OSError) as e2:
|
||||||
tty.debug(
|
tty.debug(
|
||||||
f"Did not find {specfile_name} on {buildcache_fetch_url_signed_json}",
|
f"Did not find {specfile_name} on {buildcache_fetch_url_signed_json}",
|
||||||
e1,
|
e1,
|
||||||
@@ -2640,7 +2613,6 @@ def try_direct_fetch(spec, mirrors=None):
|
|||||||
f"Did not find {specfile_name} on {buildcache_fetch_url_json}", e2, level=2
|
f"Did not find {specfile_name} on {buildcache_fetch_url_json}", e2, level=2
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
specfile_contents = codecs.getreader("utf-8")(fs).read()
|
|
||||||
|
|
||||||
# read the spec from the build cache file. All specs in build caches
|
# read the spec from the build cache file. All specs in build caches
|
||||||
# are concrete (as they are built) so we need to mark this spec
|
# are concrete (as they are built) so we need to mark this spec
|
||||||
@@ -2734,8 +2706,9 @@ def get_keys(install=False, trust=False, force=False, mirrors=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
_, _, json_file = web_util.read_from_url(keys_index)
|
_, _, json_file = web_util.read_from_url(keys_index)
|
||||||
json_index = sjson.load(codecs.getreader("utf-8")(json_file))
|
json_index = sjson.load(json_file)
|
||||||
except web_util.SpackWebError as url_err:
|
except (web_util.SpackWebError, OSError, ValueError) as url_err:
|
||||||
|
# TODO: avoid repeated request
|
||||||
if web_util.url_exists(keys_index):
|
if web_util.url_exists(keys_index):
|
||||||
tty.error(
|
tty.error(
|
||||||
f"Unable to find public keys in {url_util.format(fetch_url)},"
|
f"Unable to find public keys in {url_util.format(fetch_url)},"
|
||||||
@@ -2982,14 +2955,14 @@ def __init__(self, url, local_hash, urlopen=web_util.urlopen):
|
|||||||
|
|
||||||
def get_remote_hash(self):
|
def get_remote_hash(self):
|
||||||
# Failure to fetch index.json.hash is not fatal
|
# Failure to fetch index.json.hash is not fatal
|
||||||
url_index_hash = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, "index.json.hash")
|
url_index_hash = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, INDEX_HASH_FILE)
|
||||||
try:
|
try:
|
||||||
response = self.urlopen(urllib.request.Request(url_index_hash, headers=self.headers))
|
response = self.urlopen(urllib.request.Request(url_index_hash, headers=self.headers))
|
||||||
except (TimeoutError, urllib.error.URLError):
|
remote_hash = response.read(64)
|
||||||
|
except OSError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Validate the hash
|
# Validate the hash
|
||||||
remote_hash = response.read(64)
|
|
||||||
if not re.match(rb"[a-f\d]{64}$", remote_hash):
|
if not re.match(rb"[a-f\d]{64}$", remote_hash):
|
||||||
return None
|
return None
|
||||||
return remote_hash.decode("utf-8")
|
return remote_hash.decode("utf-8")
|
||||||
@@ -3003,17 +2976,17 @@ def conditional_fetch(self) -> FetchIndexResult:
|
|||||||
return FetchIndexResult(etag=None, hash=None, data=None, fresh=True)
|
return FetchIndexResult(etag=None, hash=None, data=None, fresh=True)
|
||||||
|
|
||||||
# Otherwise, download index.json
|
# Otherwise, download index.json
|
||||||
url_index = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, "index.json")
|
url_index = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, spack_db.INDEX_JSON_FILE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = self.urlopen(urllib.request.Request(url_index, headers=self.headers))
|
response = self.urlopen(urllib.request.Request(url_index, headers=self.headers))
|
||||||
except (TimeoutError, urllib.error.URLError) as e:
|
except OSError as e:
|
||||||
raise FetchIndexError("Could not fetch index from {}".format(url_index), e) from e
|
raise FetchIndexError(f"Could not fetch index from {url_index}", e) from e
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = codecs.getreader("utf-8")(response).read()
|
result = codecs.getreader("utf-8")(response).read()
|
||||||
except ValueError as e:
|
except (ValueError, OSError) as e:
|
||||||
raise FetchIndexError("Remote index {} is invalid".format(url_index), e) from e
|
raise FetchIndexError(f"Remote index {url_index} is invalid") from e
|
||||||
|
|
||||||
computed_hash = compute_hash(result)
|
computed_hash = compute_hash(result)
|
||||||
|
|
||||||
@@ -3047,7 +3020,7 @@ def __init__(self, url, etag, urlopen=web_util.urlopen):
|
|||||||
|
|
||||||
def conditional_fetch(self) -> FetchIndexResult:
|
def conditional_fetch(self) -> FetchIndexResult:
|
||||||
# Just do a conditional fetch immediately
|
# Just do a conditional fetch immediately
|
||||||
url = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, "index.json")
|
url = url_util.join(self.url, BUILD_CACHE_RELATIVE_PATH, spack_db.INDEX_JSON_FILE)
|
||||||
headers = {"User-Agent": web_util.SPACK_USER_AGENT, "If-None-Match": f'"{self.etag}"'}
|
headers = {"User-Agent": web_util.SPACK_USER_AGENT, "If-None-Match": f'"{self.etag}"'}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -3057,12 +3030,12 @@ def conditional_fetch(self) -> FetchIndexResult:
|
|||||||
# Not modified; that means fresh.
|
# Not modified; that means fresh.
|
||||||
return FetchIndexResult(etag=None, hash=None, data=None, fresh=True)
|
return FetchIndexResult(etag=None, hash=None, data=None, fresh=True)
|
||||||
raise FetchIndexError(f"Could not fetch index {url}", e) from e
|
raise FetchIndexError(f"Could not fetch index {url}", e) from e
|
||||||
except (TimeoutError, urllib.error.URLError) as e:
|
except OSError as e: # URLError, socket.timeout, etc.
|
||||||
raise FetchIndexError(f"Could not fetch index {url}", e) from e
|
raise FetchIndexError(f"Could not fetch index {url}", e) from e
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = codecs.getreader("utf-8")(response).read()
|
result = codecs.getreader("utf-8")(response).read()
|
||||||
except ValueError as e:
|
except (ValueError, OSError) as e:
|
||||||
raise FetchIndexError(f"Remote index {url} is invalid", e) from e
|
raise FetchIndexError(f"Remote index {url} is invalid", e) from e
|
||||||
|
|
||||||
headers = response.headers
|
headers = response.headers
|
||||||
@@ -3094,11 +3067,11 @@ def conditional_fetch(self) -> FetchIndexResult:
|
|||||||
headers={"Accept": "application/vnd.oci.image.manifest.v1+json"},
|
headers={"Accept": "application/vnd.oci.image.manifest.v1+json"},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except (TimeoutError, urllib.error.URLError) as e:
|
except OSError as e:
|
||||||
raise FetchIndexError(f"Could not fetch manifest from {url_manifest}", e) from e
|
raise FetchIndexError(f"Could not fetch manifest from {url_manifest}", e) from e
|
||||||
|
|
||||||
try:
|
try:
|
||||||
manifest = json.loads(response.read())
|
manifest = json.load(response)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise FetchIndexError(f"Remote index {url_manifest} is invalid", e) from e
|
raise FetchIndexError(f"Remote index {url_manifest} is invalid", e) from e
|
||||||
|
|
||||||
@@ -3113,14 +3086,16 @@ def conditional_fetch(self) -> FetchIndexResult:
|
|||||||
return FetchIndexResult(etag=None, hash=None, data=None, fresh=True)
|
return FetchIndexResult(etag=None, hash=None, data=None, fresh=True)
|
||||||
|
|
||||||
# Otherwise fetch the blob / index.json
|
# Otherwise fetch the blob / index.json
|
||||||
response = self.urlopen(
|
try:
|
||||||
urllib.request.Request(
|
response = self.urlopen(
|
||||||
url=self.ref.blob_url(index_digest),
|
urllib.request.Request(
|
||||||
headers={"Accept": "application/vnd.oci.image.layer.v1.tar+gzip"},
|
url=self.ref.blob_url(index_digest),
|
||||||
|
headers={"Accept": "application/vnd.oci.image.layer.v1.tar+gzip"},
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
result = codecs.getreader("utf-8")(response).read()
|
||||||
|
except (OSError, ValueError) as e:
|
||||||
result = codecs.getreader("utf-8")(response).read()
|
raise FetchIndexError(f"Remote index {url_manifest} is invalid", e) from e
|
||||||
|
|
||||||
# Make sure the blob we download has the advertised hash
|
# Make sure the blob we download has the advertised hash
|
||||||
if compute_hash(result) != index_digest.digest:
|
if compute_hash(result) != index_digest.digest:
|
||||||
|
@@ -5,12 +5,14 @@
|
|||||||
import fnmatch
|
import fnmatch
|
||||||
import glob
|
import glob
|
||||||
import importlib
|
import importlib
|
||||||
import os.path
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Dict, Optional, Sequence, Union
|
from typing import Optional, Sequence, Union
|
||||||
|
|
||||||
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
import archspec.cpu
|
import archspec.cpu
|
||||||
|
|
||||||
@@ -18,13 +20,17 @@
|
|||||||
from llnl.util import tty
|
from llnl.util import tty
|
||||||
|
|
||||||
import spack.platforms
|
import spack.platforms
|
||||||
|
import spack.spec
|
||||||
import spack.store
|
import spack.store
|
||||||
import spack.util.environment
|
import spack.util.environment
|
||||||
import spack.util.executable
|
import spack.util.executable
|
||||||
|
|
||||||
from .config import spec_for_current_python
|
from .config import spec_for_current_python
|
||||||
|
|
||||||
QueryInfo = Dict[str, "spack.spec.Spec"]
|
|
||||||
|
class QueryInfo(TypedDict, total=False):
|
||||||
|
spec: spack.spec.Spec
|
||||||
|
command: spack.util.executable.Executable
|
||||||
|
|
||||||
|
|
||||||
def _python_import(module: str) -> bool:
|
def _python_import(module: str) -> bool:
|
||||||
@@ -211,7 +217,9 @@ def _executables_in_store(
|
|||||||
):
|
):
|
||||||
spack.util.environment.path_put_first("PATH", [bin_dir])
|
spack.util.environment.path_put_first("PATH", [bin_dir])
|
||||||
if query_info is not None:
|
if query_info is not None:
|
||||||
query_info["command"] = spack.util.executable.which(*executables, path=bin_dir)
|
query_info["command"] = spack.util.executable.which(
|
||||||
|
*executables, path=bin_dir, required=True
|
||||||
|
)
|
||||||
query_info["spec"] = concrete_spec
|
query_info["spec"] = concrete_spec
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@@ -27,9 +27,9 @@
|
|||||||
class ClingoBootstrapConcretizer:
|
class ClingoBootstrapConcretizer:
|
||||||
def __init__(self, configuration):
|
def __init__(self, configuration):
|
||||||
self.host_platform = spack.platforms.host()
|
self.host_platform = spack.platforms.host()
|
||||||
self.host_os = self.host_platform.operating_system("frontend")
|
self.host_os = self.host_platform.default_operating_system()
|
||||||
self.host_target = archspec.cpu.host().family
|
self.host_target = archspec.cpu.host().family
|
||||||
self.host_architecture = spack.spec.ArchSpec.frontend_arch()
|
self.host_architecture = spack.spec.ArchSpec.default_arch()
|
||||||
self.host_architecture.target = str(self.host_target)
|
self.host_architecture.target = str(self.host_target)
|
||||||
self.host_compiler = self._valid_compiler_or_raise()
|
self.host_compiler = self._valid_compiler_or_raise()
|
||||||
self.host_python = self.python_external_spec()
|
self.host_python = self.python_external_spec()
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"""Manage configuration swapping for bootstrapping purposes"""
|
"""Manage configuration swapping for bootstrapping purposes"""
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import os.path
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Dict, Generator, MutableSequence, Sequence
|
from typing import Any, Dict, Generator, MutableSequence, Sequence
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ 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.default_arch()
|
||||||
if not spack.compilers.compilers_for_arch(arch):
|
if not spack.compilers.compilers_for_arch(arch):
|
||||||
spack.compilers.find_compilers()
|
spack.compilers.find_compilers()
|
||||||
|
|
||||||
|
@@ -25,7 +25,6 @@
|
|||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Any, Callable, Dict, List, Optional, Tuple
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
||||||
@@ -34,8 +33,10 @@
|
|||||||
from llnl.util.lang import GroupedExceptionHandler
|
from llnl.util.lang import GroupedExceptionHandler
|
||||||
|
|
||||||
import spack.binary_distribution
|
import spack.binary_distribution
|
||||||
|
import spack.concretize
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.detection
|
import spack.detection
|
||||||
|
import spack.error
|
||||||
import spack.mirrors.mirror
|
import spack.mirrors.mirror
|
||||||
import spack.platforms
|
import spack.platforms
|
||||||
import spack.spec
|
import spack.spec
|
||||||
@@ -44,10 +45,17 @@
|
|||||||
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.version
|
import spack.version
|
||||||
from spack.installer import PackageInstaller
|
from spack.installer import PackageInstaller
|
||||||
|
|
||||||
from ._common import _executables_in_store, _python_import, _root_spec, _try_import_from_store
|
from ._common import (
|
||||||
|
QueryInfo,
|
||||||
|
_executables_in_store,
|
||||||
|
_python_import,
|
||||||
|
_root_spec,
|
||||||
|
_try_import_from_store,
|
||||||
|
)
|
||||||
from .clingo import ClingoBootstrapConcretizer
|
from .clingo import ClingoBootstrapConcretizer
|
||||||
from .config import spack_python_interpreter, spec_for_current_python
|
from .config import spack_python_interpreter, spec_for_current_python
|
||||||
|
|
||||||
@@ -89,8 +97,12 @@ def __init__(self, conf: ConfigDictionary) -> None:
|
|||||||
self.name = conf["name"]
|
self.name = conf["name"]
|
||||||
self.metadata_dir = spack.util.path.canonicalize_path(conf["metadata"])
|
self.metadata_dir = spack.util.path.canonicalize_path(conf["metadata"])
|
||||||
|
|
||||||
# Promote (relative) paths to file urls
|
# Check for relative paths, and turn them into absolute paths
|
||||||
self.url = spack.mirrors.mirror.Mirror(conf["info"]["url"]).fetch_url
|
# root is the metadata_dir
|
||||||
|
maybe_url = conf["info"]["url"]
|
||||||
|
if spack.util.url.is_path_instead_of_url(maybe_url) and not os.path.isabs(maybe_url):
|
||||||
|
maybe_url = os.path.join(self.metadata_dir, maybe_url)
|
||||||
|
self.url = spack.mirrors.mirror.Mirror(maybe_url).fetch_url
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mirror_scope(self) -> spack.config.InternalConfigScope:
|
def mirror_scope(self) -> spack.config.InternalConfigScope:
|
||||||
@@ -134,7 +146,7 @@ class BuildcacheBootstrapper(Bootstrapper):
|
|||||||
|
|
||||||
def __init__(self, conf) -> None:
|
def __init__(self, conf) -> None:
|
||||||
super().__init__(conf)
|
super().__init__(conf)
|
||||||
self.last_search: Optional[ConfigDictionary] = None
|
self.last_search: Optional[QueryInfo] = None
|
||||||
self.config_scope_name = f"bootstrap_buildcache-{uuid.uuid4()}"
|
self.config_scope_name = f"bootstrap_buildcache-{uuid.uuid4()}"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -211,14 +223,14 @@ def _install_and_test(
|
|||||||
for _, pkg_hash, pkg_sha256 in item["binaries"]:
|
for _, pkg_hash, pkg_sha256 in item["binaries"]:
|
||||||
self._install_by_hash(pkg_hash, pkg_sha256, bincache_platform)
|
self._install_by_hash(pkg_hash, pkg_sha256, bincache_platform)
|
||||||
|
|
||||||
info: ConfigDictionary = {}
|
info: QueryInfo = {}
|
||||||
if test_fn(query_spec=abstract_spec, query_info=info):
|
if test_fn(query_spec=abstract_spec, query_info=info):
|
||||||
self.last_search = info
|
self.last_search = info
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def try_import(self, module: str, abstract_spec_str: str) -> bool:
|
def try_import(self, module: str, abstract_spec_str: str) -> bool:
|
||||||
info: ConfigDictionary
|
info: QueryInfo
|
||||||
test_fn, info = functools.partial(_try_import_from_store, module), {}
|
test_fn, info = functools.partial(_try_import_from_store, module), {}
|
||||||
if test_fn(query_spec=abstract_spec_str, query_info=info):
|
if test_fn(query_spec=abstract_spec_str, query_info=info):
|
||||||
return True
|
return True
|
||||||
@@ -231,7 +243,7 @@ def try_import(self, module: str, abstract_spec_str: str) -> bool:
|
|||||||
return self._install_and_test(abstract_spec, bincache_platform, data, test_fn)
|
return self._install_and_test(abstract_spec, bincache_platform, data, test_fn)
|
||||||
|
|
||||||
def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bool:
|
def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bool:
|
||||||
info: ConfigDictionary
|
info: QueryInfo
|
||||||
test_fn, info = functools.partial(_executables_in_store, executables), {}
|
test_fn, info = functools.partial(_executables_in_store, executables), {}
|
||||||
if test_fn(query_spec=abstract_spec_str, query_info=info):
|
if test_fn(query_spec=abstract_spec_str, query_info=info):
|
||||||
self.last_search = info
|
self.last_search = info
|
||||||
@@ -249,11 +261,11 @@ class SourceBootstrapper(Bootstrapper):
|
|||||||
|
|
||||||
def __init__(self, conf) -> None:
|
def __init__(self, conf) -> None:
|
||||||
super().__init__(conf)
|
super().__init__(conf)
|
||||||
self.last_search: Optional[ConfigDictionary] = None
|
self.last_search: Optional[QueryInfo] = None
|
||||||
self.config_scope_name = f"bootstrap_source-{uuid.uuid4()}"
|
self.config_scope_name = f"bootstrap_source-{uuid.uuid4()}"
|
||||||
|
|
||||||
def try_import(self, module: str, abstract_spec_str: str) -> bool:
|
def try_import(self, module: str, abstract_spec_str: str) -> bool:
|
||||||
info: ConfigDictionary = {}
|
info: QueryInfo = {}
|
||||||
if _try_import_from_store(module, abstract_spec_str, query_info=info):
|
if _try_import_from_store(module, abstract_spec_str, query_info=info):
|
||||||
self.last_search = info
|
self.last_search = info
|
||||||
return True
|
return True
|
||||||
@@ -270,10 +282,10 @@ def try_import(self, module: str, abstract_spec_str: str) -> bool:
|
|||||||
bootstrapper = ClingoBootstrapConcretizer(configuration=spack.config.CONFIG)
|
bootstrapper = ClingoBootstrapConcretizer(configuration=spack.config.CONFIG)
|
||||||
concrete_spec = bootstrapper.concretize()
|
concrete_spec = bootstrapper.concretize()
|
||||||
else:
|
else:
|
||||||
concrete_spec = spack.spec.Spec(
|
abstract_spec = spack.spec.Spec(
|
||||||
abstract_spec_str + " ^" + spec_for_current_python()
|
abstract_spec_str + " ^" + spec_for_current_python()
|
||||||
)
|
)
|
||||||
concrete_spec.concretize()
|
concrete_spec = spack.concretize.concretize_one(abstract_spec)
|
||||||
|
|
||||||
msg = "[BOOTSTRAP MODULE {0}] Try installing '{1}' from sources"
|
msg = "[BOOTSTRAP MODULE {0}] Try installing '{1}' from sources"
|
||||||
tty.debug(msg.format(module, abstract_spec_str))
|
tty.debug(msg.format(module, abstract_spec_str))
|
||||||
@@ -288,7 +300,7 @@ def try_import(self, module: str, abstract_spec_str: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bool:
|
def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bool:
|
||||||
info: ConfigDictionary = {}
|
info: QueryInfo = {}
|
||||||
if _executables_in_store(executables, abstract_spec_str, query_info=info):
|
if _executables_in_store(executables, abstract_spec_str, query_info=info):
|
||||||
self.last_search = info
|
self.last_search = info
|
||||||
return True
|
return True
|
||||||
@@ -299,7 +311,7 @@ def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bo
|
|||||||
# might reduce compilation time by a fair amount
|
# might reduce compilation time by a fair amount
|
||||||
_add_externals_if_missing()
|
_add_externals_if_missing()
|
||||||
|
|
||||||
concrete_spec = spack.spec.Spec(abstract_spec_str).concretized()
|
concrete_spec = spack.concretize.concretize_one(abstract_spec_str)
|
||||||
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):
|
||||||
@@ -316,11 +328,9 @@ def create_bootstrapper(conf: ConfigDictionary):
|
|||||||
return _bootstrap_methods[btype](conf)
|
return _bootstrap_methods[btype](conf)
|
||||||
|
|
||||||
|
|
||||||
def source_is_enabled_or_raise(conf: ConfigDictionary):
|
def source_is_enabled(conf: ConfigDictionary) -> bool:
|
||||||
"""Raise ValueError if the source is not enabled for bootstrapping"""
|
"""Returns true if the source is not enabled for bootstrapping"""
|
||||||
trusted, name = spack.config.get("bootstrap:trusted"), conf["name"]
|
return spack.config.get("bootstrap:trusted").get(conf["name"], False)
|
||||||
if not trusted.get(name, False):
|
|
||||||
raise ValueError("source is not trusted")
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_module_importable_or_raise(module: str, abstract_spec: Optional[str] = None):
|
def ensure_module_importable_or_raise(module: str, abstract_spec: Optional[str] = None):
|
||||||
@@ -350,24 +360,23 @@ def ensure_module_importable_or_raise(module: str, abstract_spec: Optional[str]
|
|||||||
exception_handler = GroupedExceptionHandler()
|
exception_handler = GroupedExceptionHandler()
|
||||||
|
|
||||||
for current_config in bootstrapping_sources():
|
for current_config in bootstrapping_sources():
|
||||||
|
if not source_is_enabled(current_config):
|
||||||
|
continue
|
||||||
with exception_handler.forward(current_config["name"], Exception):
|
with exception_handler.forward(current_config["name"], Exception):
|
||||||
source_is_enabled_or_raise(current_config)
|
if create_bootstrapper(current_config).try_import(module, abstract_spec):
|
||||||
current_bootstrapper = create_bootstrapper(current_config)
|
|
||||||
if current_bootstrapper.try_import(module, abstract_spec):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
assert exception_handler, (
|
|
||||||
f"expected at least one exception to have been raised at this point: "
|
|
||||||
f"while bootstrapping {module}"
|
|
||||||
)
|
|
||||||
msg = f'cannot bootstrap the "{module}" Python module '
|
msg = f'cannot bootstrap the "{module}" Python module '
|
||||||
if abstract_spec:
|
if abstract_spec:
|
||||||
msg += f'from spec "{abstract_spec}" '
|
msg += f'from spec "{abstract_spec}" '
|
||||||
if tty.is_debug():
|
|
||||||
|
if not exception_handler:
|
||||||
|
msg += ": no bootstrapping sources are enabled"
|
||||||
|
elif spack.error.debug or spack.error.SHOW_BACKTRACE:
|
||||||
msg += exception_handler.grouped_message(with_tracebacks=True)
|
msg += exception_handler.grouped_message(with_tracebacks=True)
|
||||||
else:
|
else:
|
||||||
msg += exception_handler.grouped_message(with_tracebacks=False)
|
msg += exception_handler.grouped_message(with_tracebacks=False)
|
||||||
msg += "\nRun `spack --debug ...` for more detailed errors"
|
msg += "\nRun `spack --backtrace ...` for more detailed errors"
|
||||||
raise ImportError(msg)
|
raise ImportError(msg)
|
||||||
|
|
||||||
|
|
||||||
@@ -405,8 +414,9 @@ def ensure_executables_in_path_or_raise(
|
|||||||
exception_handler = GroupedExceptionHandler()
|
exception_handler = GroupedExceptionHandler()
|
||||||
|
|
||||||
for current_config in bootstrapping_sources():
|
for current_config in bootstrapping_sources():
|
||||||
|
if not source_is_enabled(current_config):
|
||||||
|
continue
|
||||||
with exception_handler.forward(current_config["name"], Exception):
|
with exception_handler.forward(current_config["name"], Exception):
|
||||||
source_is_enabled_or_raise(current_config)
|
|
||||||
current_bootstrapper = create_bootstrapper(current_config)
|
current_bootstrapper = create_bootstrapper(current_config)
|
||||||
if current_bootstrapper.try_search_path(executables, abstract_spec):
|
if current_bootstrapper.try_search_path(executables, abstract_spec):
|
||||||
# Additional environment variables needed
|
# Additional environment variables needed
|
||||||
@@ -414,6 +424,7 @@ def ensure_executables_in_path_or_raise(
|
|||||||
current_bootstrapper.last_search["spec"],
|
current_bootstrapper.last_search["spec"],
|
||||||
current_bootstrapper.last_search["command"],
|
current_bootstrapper.last_search["command"],
|
||||||
)
|
)
|
||||||
|
assert cmd is not None, "expected an Executable"
|
||||||
cmd.add_default_envmod(
|
cmd.add_default_envmod(
|
||||||
spack.user_environment.environment_modifications_for_specs(
|
spack.user_environment.environment_modifications_for_specs(
|
||||||
concrete_spec, set_package_py_globals=False
|
concrete_spec, set_package_py_globals=False
|
||||||
@@ -421,18 +432,17 @@ def ensure_executables_in_path_or_raise(
|
|||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
assert exception_handler, (
|
|
||||||
f"expected at least one exception to have been raised at this point: "
|
|
||||||
f"while bootstrapping {executables_str}"
|
|
||||||
)
|
|
||||||
msg = f"cannot bootstrap any of the {executables_str} executables "
|
msg = f"cannot bootstrap any of the {executables_str} executables "
|
||||||
if abstract_spec:
|
if abstract_spec:
|
||||||
msg += f'from spec "{abstract_spec}" '
|
msg += f'from spec "{abstract_spec}" '
|
||||||
if tty.is_debug():
|
|
||||||
|
if not exception_handler:
|
||||||
|
msg += ": no bootstrapping sources are enabled"
|
||||||
|
elif spack.error.debug or spack.error.SHOW_BACKTRACE:
|
||||||
msg += exception_handler.grouped_message(with_tracebacks=True)
|
msg += exception_handler.grouped_message(with_tracebacks=True)
|
||||||
else:
|
else:
|
||||||
msg += exception_handler.grouped_message(with_tracebacks=False)
|
msg += exception_handler.grouped_message(with_tracebacks=False)
|
||||||
msg += "\nRun `spack --debug ...` for more detailed errors"
|
msg += "\nRun `spack --backtrace ...` for more detailed errors"
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -63,7 +63,6 @@ def _missing(name: str, purpose: str, system_only: bool = True) -> str:
|
|||||||
|
|
||||||
def _core_requirements() -> List[RequiredResponseType]:
|
def _core_requirements() -> List[RequiredResponseType]:
|
||||||
_core_system_exes = {
|
_core_system_exes = {
|
||||||
"make": _missing("make", "required to build software from sources"),
|
|
||||||
"patch": _missing("patch", "required to patch source code before building"),
|
"patch": _missing("patch", "required to patch source code before building"),
|
||||||
"tar": _missing("tar", "required to manage code archives"),
|
"tar": _missing("tar", "required to manage code archives"),
|
||||||
"gzip": _missing("gzip", "required to compress/decompress code archives"),
|
"gzip": _missing("gzip", "required to compress/decompress code archives"),
|
||||||
|
@@ -44,7 +44,19 @@
|
|||||||
from enum import Flag, auto
|
from enum import Flag, auto
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from multiprocessing.connection import Connection
|
from multiprocessing.connection import Connection
|
||||||
from typing import Callable, Dict, List, Optional, Set, Tuple
|
from typing import (
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
Set,
|
||||||
|
TextIO,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
Union,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
|
||||||
import archspec.cpu
|
import archspec.cpu
|
||||||
|
|
||||||
@@ -146,48 +158,128 @@ def get_effective_jobs(jobs, parallel=True, supports_jobserver=False):
|
|||||||
|
|
||||||
|
|
||||||
class MakeExecutable(Executable):
|
class MakeExecutable(Executable):
|
||||||
"""Special callable executable object for make so the user can specify
|
"""Special callable executable object for make so the user can specify parallelism options
|
||||||
parallelism options on a per-invocation basis. Specifying
|
on a per-invocation basis.
|
||||||
'parallel' to the call will override whatever the package's
|
|
||||||
global setting is, so you can either default to true or false and
|
|
||||||
override particular calls. Specifying 'jobs_env' to a particular
|
|
||||||
call will name an environment variable which will be set to the
|
|
||||||
parallelism level (without affecting the normal invocation with
|
|
||||||
-j).
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, jobs, **kwargs):
|
def __init__(self, name: str, *, jobs: int, supports_jobserver: bool = True) -> None:
|
||||||
supports_jobserver = kwargs.pop("supports_jobserver", True)
|
super().__init__(name)
|
||||||
super().__init__(name, **kwargs)
|
|
||||||
self.supports_jobserver = supports_jobserver
|
self.supports_jobserver = supports_jobserver
|
||||||
self.jobs = jobs
|
self.jobs = jobs
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
@overload
|
||||||
"""parallel, and jobs_env from kwargs are swallowed and used here;
|
def __call__(
|
||||||
remaining arguments are passed through to the superclass.
|
self,
|
||||||
"""
|
*args: str,
|
||||||
parallel = kwargs.pop("parallel", True)
|
parallel: bool = ...,
|
||||||
jobs_env = kwargs.pop("jobs_env", None)
|
jobs_env: Optional[str] = ...,
|
||||||
jobs_env_supports_jobserver = kwargs.pop("jobs_env_supports_jobserver", False)
|
jobs_env_supports_jobserver: bool = ...,
|
||||||
|
fail_on_error: bool = ...,
|
||||||
|
ignore_errors: Union[int, Sequence[int]] = ...,
|
||||||
|
ignore_quotes: Optional[bool] = ...,
|
||||||
|
timeout: Optional[int] = ...,
|
||||||
|
env: Optional[Union[Dict[str, str], EnvironmentModifications]] = ...,
|
||||||
|
extra_env: Optional[Union[Dict[str, str], EnvironmentModifications]] = ...,
|
||||||
|
input: Optional[TextIO] = ...,
|
||||||
|
output: Union[Optional[TextIO], str] = ...,
|
||||||
|
error: Union[Optional[TextIO], str] = ...,
|
||||||
|
_dump_env: Optional[Dict[str, str]] = ...,
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
*args: str,
|
||||||
|
parallel: bool = ...,
|
||||||
|
jobs_env: Optional[str] = ...,
|
||||||
|
jobs_env_supports_jobserver: bool = ...,
|
||||||
|
fail_on_error: bool = ...,
|
||||||
|
ignore_errors: Union[int, Sequence[int]] = ...,
|
||||||
|
ignore_quotes: Optional[bool] = ...,
|
||||||
|
timeout: Optional[int] = ...,
|
||||||
|
env: Optional[Union[Dict[str, str], EnvironmentModifications]] = ...,
|
||||||
|
extra_env: Optional[Union[Dict[str, str], EnvironmentModifications]] = ...,
|
||||||
|
input: Optional[TextIO] = ...,
|
||||||
|
output: Union[Type[str], Callable] = ...,
|
||||||
|
error: Union[Optional[TextIO], str, Type[str], Callable] = ...,
|
||||||
|
_dump_env: Optional[Dict[str, str]] = ...,
|
||||||
|
) -> str: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
*args: str,
|
||||||
|
parallel: bool = ...,
|
||||||
|
jobs_env: Optional[str] = ...,
|
||||||
|
jobs_env_supports_jobserver: bool = ...,
|
||||||
|
fail_on_error: bool = ...,
|
||||||
|
ignore_errors: Union[int, Sequence[int]] = ...,
|
||||||
|
ignore_quotes: Optional[bool] = ...,
|
||||||
|
timeout: Optional[int] = ...,
|
||||||
|
env: Optional[Union[Dict[str, str], EnvironmentModifications]] = ...,
|
||||||
|
extra_env: Optional[Union[Dict[str, str], EnvironmentModifications]] = ...,
|
||||||
|
input: Optional[TextIO] = ...,
|
||||||
|
output: Union[Optional[TextIO], str, Type[str], Callable] = ...,
|
||||||
|
error: Union[Type[str], Callable] = ...,
|
||||||
|
_dump_env: Optional[Dict[str, str]] = ...,
|
||||||
|
) -> str: ...
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
*args: str,
|
||||||
|
parallel: bool = True,
|
||||||
|
jobs_env: Optional[str] = None,
|
||||||
|
jobs_env_supports_jobserver: bool = False,
|
||||||
|
**kwargs,
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""Runs this "make" executable in a subprocess.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parallel: if False, parallelism is disabled
|
||||||
|
jobs_env: environment variable that will be set to the current level of parallelism
|
||||||
|
jobs_env_supports_jobserver: whether the jobs env supports a job server
|
||||||
|
|
||||||
|
For all the other **kwargs, refer to the base class.
|
||||||
|
"""
|
||||||
jobs = get_effective_jobs(
|
jobs = get_effective_jobs(
|
||||||
self.jobs, parallel=parallel, supports_jobserver=self.supports_jobserver
|
self.jobs, parallel=parallel, supports_jobserver=self.supports_jobserver
|
||||||
)
|
)
|
||||||
if jobs is not None:
|
if jobs is not None:
|
||||||
args = ("-j{0}".format(jobs),) + args
|
args = (f"-j{jobs}",) + args
|
||||||
|
|
||||||
if jobs_env:
|
if jobs_env:
|
||||||
# Caller wants us to set an environment variable to
|
# Caller wants us to set an environment variable to control the parallelism
|
||||||
# control the parallelism.
|
|
||||||
jobs_env_jobs = get_effective_jobs(
|
jobs_env_jobs = get_effective_jobs(
|
||||||
self.jobs, parallel=parallel, supports_jobserver=jobs_env_supports_jobserver
|
self.jobs, parallel=parallel, supports_jobserver=jobs_env_supports_jobserver
|
||||||
)
|
)
|
||||||
if jobs_env_jobs is not None:
|
if jobs_env_jobs is not None:
|
||||||
kwargs["extra_env"] = {jobs_env: str(jobs_env_jobs)}
|
extra_env = kwargs.setdefault("extra_env", {})
|
||||||
|
extra_env.update({jobs_env: str(jobs_env_jobs)})
|
||||||
|
|
||||||
return super().__call__(*args, **kwargs)
|
return super().__call__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class UndeclaredDependencyError(spack.error.SpackError):
|
||||||
|
"""Raised if a dependency is invoking an executable through a module global, without
|
||||||
|
declaring a dependency on it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedExecutable:
|
||||||
|
def __init__(self, pkg: str, exe: str, exe_pkg: str) -> None:
|
||||||
|
self.pkg = pkg
|
||||||
|
self.exe = exe
|
||||||
|
self.exe_pkg = exe_pkg
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
raise UndeclaredDependencyError(
|
||||||
|
f"{self.pkg} is using {self.exe} without declaring a dependency on {self.exe_pkg}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_default_env(self, key: str, value: str):
|
||||||
|
self.__call__()
|
||||||
|
|
||||||
|
|
||||||
def clean_environment():
|
def clean_environment():
|
||||||
# Stuff in here sanitizes the build environment to eliminate
|
# Stuff in here sanitizes the build environment to eliminate
|
||||||
# anything the user has set that may interfere. We apply it immediately
|
# anything the user has set that may interfere. We apply it immediately
|
||||||
@@ -209,11 +301,13 @@ def clean_environment():
|
|||||||
env.unset("CPLUS_INCLUDE_PATH")
|
env.unset("CPLUS_INCLUDE_PATH")
|
||||||
env.unset("OBJC_INCLUDE_PATH")
|
env.unset("OBJC_INCLUDE_PATH")
|
||||||
|
|
||||||
|
# prevent configure scripts from sourcing variables from config site file (AC_SITE_LOAD).
|
||||||
|
env.set("CONFIG_SITE", os.devnull)
|
||||||
env.unset("CMAKE_PREFIX_PATH")
|
env.unset("CMAKE_PREFIX_PATH")
|
||||||
|
|
||||||
env.unset("PYTHONPATH")
|
env.unset("PYTHONPATH")
|
||||||
env.unset("R_HOME")
|
env.unset("R_HOME")
|
||||||
env.unset("R_ENVIRON")
|
env.unset("R_ENVIRON")
|
||||||
|
|
||||||
env.unset("LUA_PATH")
|
env.unset("LUA_PATH")
|
||||||
env.unset("LUA_CPATH")
|
env.unset("LUA_CPATH")
|
||||||
|
|
||||||
@@ -621,10 +715,9 @@ 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)
|
||||||
|
|
||||||
# TODO: make these build deps that can be installed if not found.
|
module.make = DeprecatedExecutable(pkg.name, "make", "gmake")
|
||||||
module.make = MakeExecutable("make", jobs)
|
module.gmake = DeprecatedExecutable(pkg.name, "gmake", "gmake")
|
||||||
module.gmake = MakeExecutable("gmake", jobs)
|
module.ninja = DeprecatedExecutable(pkg.name, "ninja", "ninja")
|
||||||
module.ninja = MakeExecutable("ninja", jobs, supports_jobserver=False)
|
|
||||||
# TODO: johnwparent: add package or builder support to define these build tools
|
# TODO: johnwparent: add package or builder support to define these build tools
|
||||||
# for now there is no entrypoint for builders to define these on their
|
# for now there is no entrypoint for builders to define these on their
|
||||||
# own
|
# own
|
||||||
|
@@ -6,7 +6,9 @@
|
|||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
|
|
||||||
import spack.directives
|
import spack.directives
|
||||||
|
import spack.spec
|
||||||
import spack.util.executable
|
import spack.util.executable
|
||||||
|
import spack.util.prefix
|
||||||
|
|
||||||
from .autotools import AutotoolsBuilder, AutotoolsPackage
|
from .autotools import AutotoolsBuilder, AutotoolsPackage
|
||||||
|
|
||||||
@@ -17,19 +19,18 @@ class AspellBuilder(AutotoolsBuilder):
|
|||||||
to the Aspell extensions.
|
to the Aspell extensions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def configure(self, pkg, spec, prefix):
|
def configure(
|
||||||
|
self,
|
||||||
|
pkg: "AspellDictPackage", # type: ignore[override]
|
||||||
|
spec: spack.spec.Spec,
|
||||||
|
prefix: spack.util.prefix.Prefix,
|
||||||
|
):
|
||||||
aspell = spec["aspell"].prefix.bin.aspell
|
aspell = spec["aspell"].prefix.bin.aspell
|
||||||
prezip = spec["aspell"].prefix.bin.prezip
|
prezip = spec["aspell"].prefix.bin.prezip
|
||||||
destdir = prefix
|
destdir = prefix
|
||||||
|
|
||||||
sh = spack.util.executable.which("sh")
|
sh = spack.util.executable.Executable("/bin/sh")
|
||||||
sh(
|
sh("./configure", "--vars", f"ASPELL={aspell}", f"PREZIP={prezip}", f"DESTDIR={destdir}")
|
||||||
"./configure",
|
|
||||||
"--vars",
|
|
||||||
"ASPELL={0}".format(aspell),
|
|
||||||
"PREZIP={0}".format(prezip),
|
|
||||||
"DESTDIR={0}".format(destdir),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Aspell dictionaries install their bits into their prefix.lib
|
# Aspell dictionaries install their bits into their prefix.lib
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import Callable, List, Optional, Set, Tuple, Union
|
from typing import Callable, List, Optional, Set, Tuple, Union
|
||||||
@@ -356,6 +355,13 @@ def _do_patch_libtool_configure(self) -> None:
|
|||||||
)
|
)
|
||||||
# Support Libtool 2.4.2 and older:
|
# Support Libtool 2.4.2 and older:
|
||||||
x.filter(regex=r'^(\s*test \$p = "-R")(; then\s*)$', repl=r'\1 || test x-l = x"$p"\2')
|
x.filter(regex=r'^(\s*test \$p = "-R")(; then\s*)$', repl=r'\1 || test x-l = x"$p"\2')
|
||||||
|
# Configure scripts generated with libtool < 2.5.4 have a faulty test for the
|
||||||
|
# -single_module linker flag. A deprecation warning makes it think the default is
|
||||||
|
# -multi_module, triggering it to use problematic linker flags (such as ld -r). The
|
||||||
|
# linker default is `-single_module` from (ancient) macOS 10.4, so override by setting
|
||||||
|
# `lt_cv_apple_cc_single_mod=yes`. See the fix in libtool commit
|
||||||
|
# 82f7f52123e4e7e50721049f7fa6f9b870e09c9d.
|
||||||
|
x.filter("lt_cv_apple_cc_single_mod=no", "lt_cv_apple_cc_single_mod=yes", string=True)
|
||||||
|
|
||||||
@spack.phase_callbacks.run_after("configure")
|
@spack.phase_callbacks.run_after("configure")
|
||||||
def _do_patch_libtool(self) -> None:
|
def _do_patch_libtool(self) -> None:
|
||||||
@@ -527,7 +533,7 @@ def build_directory(self) -> str:
|
|||||||
return build_dir
|
return build_dir
|
||||||
|
|
||||||
@spack.phase_callbacks.run_before("autoreconf")
|
@spack.phase_callbacks.run_before("autoreconf")
|
||||||
def delete_configure_to_force_update(self) -> None:
|
def _delete_configure_to_force_update(self) -> None:
|
||||||
if self.force_autoreconf:
|
if self.force_autoreconf:
|
||||||
fs.force_remove(self.configure_abs_path)
|
fs.force_remove(self.configure_abs_path)
|
||||||
|
|
||||||
@@ -540,7 +546,7 @@ def autoreconf_search_path_args(self) -> List[str]:
|
|||||||
return _autoreconf_search_path_args(self.spec)
|
return _autoreconf_search_path_args(self.spec)
|
||||||
|
|
||||||
@spack.phase_callbacks.run_after("autoreconf")
|
@spack.phase_callbacks.run_after("autoreconf")
|
||||||
def set_configure_or_die(self) -> None:
|
def _set_configure_or_die(self) -> None:
|
||||||
"""Ensure the presence of a "configure" script, or raise. If the "configure"
|
"""Ensure the presence of a "configure" script, or raise. If the "configure"
|
||||||
is found, a module level attribute is set.
|
is found, a module level attribute is set.
|
||||||
|
|
||||||
@@ -564,10 +570,7 @@ def configure_args(self) -> List[str]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def autoreconf(
|
def autoreconf(
|
||||||
self,
|
self, pkg: AutotoolsPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Not needed usually, configure should be already there"""
|
"""Not needed usually, configure should be already there"""
|
||||||
|
|
||||||
@@ -596,10 +599,7 @@ def autoreconf(
|
|||||||
self.pkg.module.autoreconf(*autoreconf_args)
|
self.pkg.module.autoreconf(*autoreconf_args)
|
||||||
|
|
||||||
def configure(
|
def configure(
|
||||||
self,
|
self, pkg: AutotoolsPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run "configure", with the arguments specified by the builder and an
|
"""Run "configure", with the arguments specified by the builder and an
|
||||||
appropriately set prefix.
|
appropriately set prefix.
|
||||||
@@ -612,10 +612,7 @@ def configure(
|
|||||||
pkg.module.configure(*options)
|
pkg.module.configure(*options)
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
self,
|
self, pkg: AutotoolsPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run "make" on the build targets specified by the builder."""
|
"""Run "make" on the build targets specified by the builder."""
|
||||||
# See https://autotools.io/automake/silent.html
|
# See https://autotools.io/automake/silent.html
|
||||||
@@ -625,10 +622,7 @@ def build(
|
|||||||
pkg.module.make(*params)
|
pkg.module.make(*params)
|
||||||
|
|
||||||
def install(
|
def install(
|
||||||
self,
|
self, pkg: AutotoolsPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""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):
|
||||||
@@ -825,7 +819,7 @@ def installcheck(self) -> None:
|
|||||||
self.pkg._if_make_target_execute("installcheck")
|
self.pkg._if_make_target_execute("installcheck")
|
||||||
|
|
||||||
@spack.phase_callbacks.run_after("install")
|
@spack.phase_callbacks.run_after("install")
|
||||||
def remove_libtool_archives(self) -> None:
|
def _remove_libtool_archives(self) -> None:
|
||||||
"""Remove all .la files in prefix sub-folders if the package sets
|
"""Remove all .la files in prefix sub-folders if the package sets
|
||||||
``install_libtool_archives`` to be False.
|
``install_libtool_archives`` to be False.
|
||||||
"""
|
"""
|
||||||
|
@@ -10,6 +10,8 @@
|
|||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
|
|
||||||
from .cmake import CMakeBuilder, CMakePackage
|
from .cmake import CMakeBuilder, CMakePackage
|
||||||
|
|
||||||
@@ -293,6 +295,13 @@ def initconfig_hardware_entries(self):
|
|||||||
entries.append(cmake_cache_string("AMDGPU_TARGETS", arch_str))
|
entries.append(cmake_cache_string("AMDGPU_TARGETS", arch_str))
|
||||||
entries.append(cmake_cache_string("GPU_TARGETS", arch_str))
|
entries.append(cmake_cache_string("GPU_TARGETS", arch_str))
|
||||||
|
|
||||||
|
if spec.satisfies("%gcc"):
|
||||||
|
entries.append(
|
||||||
|
cmake_cache_string(
|
||||||
|
"CMAKE_HIP_FLAGS", f"--gcc-toolchain={self.pkg.compiler.prefix}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
def std_initconfig_entries(self):
|
def std_initconfig_entries(self):
|
||||||
@@ -323,7 +332,9 @@ def initconfig_package_entries(self):
|
|||||||
"""This method is to be overwritten by the package"""
|
"""This method is to be overwritten by the package"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def initconfig(self, pkg, spec, prefix):
|
def initconfig(
|
||||||
|
self, pkg: "CachedCMakePackage", spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
cache_entries = (
|
cache_entries = (
|
||||||
self.std_initconfig_entries()
|
self.std_initconfig_entries()
|
||||||
+ self.initconfig_compiler_entries()
|
+ self.initconfig_compiler_entries()
|
||||||
|
@@ -7,6 +7,8 @@
|
|||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on
|
from spack.directives import build_system, depends_on
|
||||||
from spack.multimethod import when
|
from spack.multimethod import when
|
||||||
|
|
||||||
@@ -81,12 +83,16 @@ def check_args(self):
|
|||||||
def setup_build_environment(self, env):
|
def setup_build_environment(self, env):
|
||||||
env.set("CARGO_HOME", self.stage.path)
|
env.set("CARGO_HOME", self.stage.path)
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: CargoPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""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)
|
pkg.module.cargo("install", "--root", "out", "--path", ".", *self.build_args)
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: CargoPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Copy build files into package prefix."""
|
"""Copy build files into package prefix."""
|
||||||
with fs.working_dir(self.build_directory):
|
with fs.working_dir(self.build_directory):
|
||||||
fs.install_tree("out", prefix)
|
fs.install_tree("out", prefix)
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
from typing import Any, List, Optional, Tuple
|
from typing import Any, List, Optional, Tuple
|
||||||
|
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
|
from llnl.util import tty
|
||||||
from llnl.util.lang import stable_partition
|
from llnl.util.lang import stable_partition
|
||||||
|
|
||||||
import spack.builder
|
import spack.builder
|
||||||
@@ -454,18 +455,27 @@ def cmake_args(self) -> List[str]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def cmake(
|
def cmake(
|
||||||
self,
|
self, pkg: CMakePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Runs ``cmake`` in the build directory"""
|
"""Runs ``cmake`` in the build directory"""
|
||||||
|
|
||||||
# skip cmake phase if it is an incremental develop build
|
if spec.is_develop:
|
||||||
if spec.is_develop and os.path.isfile(
|
# skip cmake phase if it is an incremental develop build
|
||||||
os.path.join(self.build_directory, "CMakeCache.txt")
|
|
||||||
):
|
# Determine the files that will re-run CMake that are generated from a successful
|
||||||
return
|
# configure step based on state
|
||||||
|
primary_generator = _extract_primary_generator(self.generator)
|
||||||
|
configure_artifact = "Makefile"
|
||||||
|
if primary_generator == "Ninja":
|
||||||
|
configure_artifact = "ninja.build"
|
||||||
|
|
||||||
|
if os.path.isfile(os.path.join(self.build_directory, configure_artifact)):
|
||||||
|
tty.msg(
|
||||||
|
"Incremental build criteria satisfied."
|
||||||
|
"Skipping CMake configure step. To force configuration run"
|
||||||
|
f" `spack clean {pkg.name}`"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
options = self.std_cmake_args
|
options = self.std_cmake_args
|
||||||
options += self.cmake_args()
|
options += self.cmake_args()
|
||||||
@@ -474,10 +484,7 @@ def cmake(
|
|||||||
pkg.module.cmake(*options)
|
pkg.module.cmake(*options)
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
self,
|
self, pkg: CMakePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Make the build targets"""
|
"""Make the build targets"""
|
||||||
with fs.working_dir(self.build_directory):
|
with fs.working_dir(self.build_directory):
|
||||||
@@ -488,10 +495,7 @@ def build(
|
|||||||
pkg.module.ninja(*self.build_targets)
|
pkg.module.ninja(*self.build_targets)
|
||||||
|
|
||||||
def install(
|
def install(
|
||||||
self,
|
self, pkg: CMakePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Make the install targets"""
|
"""Make the install targets"""
|
||||||
with fs.working_dir(self.build_directory):
|
with fs.working_dir(self.build_directory):
|
||||||
|
@@ -15,7 +15,7 @@ class CudaPackage(PackageBase):
|
|||||||
"""Auxiliary class which contains CUDA variant, dependencies and conflicts
|
"""Auxiliary class which contains CUDA variant, dependencies and conflicts
|
||||||
and is meant to unify and facilitate its usage.
|
and is meant to unify and facilitate its usage.
|
||||||
|
|
||||||
Maintainers: ax3l, Rombur, davidbeckingsale
|
Maintainers: ax3l, Rombur, davidbeckingsale, pauleonix
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#gpu-feature-list
|
# https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#gpu-feature-list
|
||||||
@@ -47,6 +47,12 @@ class CudaPackage(PackageBase):
|
|||||||
"89",
|
"89",
|
||||||
"90",
|
"90",
|
||||||
"90a",
|
"90a",
|
||||||
|
"100",
|
||||||
|
"100a",
|
||||||
|
"101",
|
||||||
|
"101a",
|
||||||
|
"120",
|
||||||
|
"120a",
|
||||||
)
|
)
|
||||||
|
|
||||||
# FIXME: keep cuda and cuda_arch separate to make usage easier until
|
# FIXME: keep cuda and cuda_arch separate to make usage easier until
|
||||||
@@ -99,39 +105,56 @@ def compute_capabilities(arch_list: Iterable[str]) -> List[str]:
|
|||||||
# CUDA version vs Architecture
|
# CUDA version vs Architecture
|
||||||
# https://en.wikipedia.org/wiki/CUDA#GPUs_supported
|
# https://en.wikipedia.org/wiki/CUDA#GPUs_supported
|
||||||
# https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html#deprecated-features
|
# https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html#deprecated-features
|
||||||
|
# Tesla support:
|
||||||
depends_on("cuda@:6.0", when="cuda_arch=10")
|
depends_on("cuda@:6.0", when="cuda_arch=10")
|
||||||
depends_on("cuda@:6.5", when="cuda_arch=11")
|
depends_on("cuda@:6.5", when="cuda_arch=11")
|
||||||
depends_on("cuda@2.1:6.5", when="cuda_arch=12")
|
depends_on("cuda@2.1:6.5", when="cuda_arch=12")
|
||||||
depends_on("cuda@2.1:6.5", when="cuda_arch=13")
|
depends_on("cuda@2.1:6.5", when="cuda_arch=13")
|
||||||
|
|
||||||
|
# Fermi support:
|
||||||
depends_on("cuda@3.0:8.0", when="cuda_arch=20")
|
depends_on("cuda@3.0:8.0", when="cuda_arch=20")
|
||||||
depends_on("cuda@3.2:8.0", when="cuda_arch=21")
|
depends_on("cuda@3.2:8.0", when="cuda_arch=21")
|
||||||
|
|
||||||
|
# Kepler support:
|
||||||
depends_on("cuda@5.0:10.2", when="cuda_arch=30")
|
depends_on("cuda@5.0:10.2", when="cuda_arch=30")
|
||||||
depends_on("cuda@5.0:10.2", when="cuda_arch=32")
|
depends_on("cuda@5.0:10.2", when="cuda_arch=32")
|
||||||
depends_on("cuda@5.0:11.8", when="cuda_arch=35")
|
depends_on("cuda@5.0:11.8", when="cuda_arch=35")
|
||||||
depends_on("cuda@6.5:11.8", when="cuda_arch=37")
|
depends_on("cuda@6.5:11.8", when="cuda_arch=37")
|
||||||
|
|
||||||
|
# Maxwell support:
|
||||||
depends_on("cuda@6.0:", when="cuda_arch=50")
|
depends_on("cuda@6.0:", when="cuda_arch=50")
|
||||||
depends_on("cuda@6.5:", when="cuda_arch=52")
|
depends_on("cuda@6.5:", when="cuda_arch=52")
|
||||||
depends_on("cuda@6.5:", when="cuda_arch=53")
|
depends_on("cuda@6.5:", when="cuda_arch=53")
|
||||||
|
|
||||||
|
# Pascal support:
|
||||||
depends_on("cuda@8.0:", when="cuda_arch=60")
|
depends_on("cuda@8.0:", when="cuda_arch=60")
|
||||||
depends_on("cuda@8.0:", when="cuda_arch=61")
|
depends_on("cuda@8.0:", when="cuda_arch=61")
|
||||||
depends_on("cuda@8.0:", when="cuda_arch=62")
|
depends_on("cuda@8.0:", when="cuda_arch=62")
|
||||||
|
|
||||||
|
# Volta support:
|
||||||
depends_on("cuda@9.0:", when="cuda_arch=70")
|
depends_on("cuda@9.0:", when="cuda_arch=70")
|
||||||
|
# Turing support:
|
||||||
depends_on("cuda@9.0:", when="cuda_arch=72")
|
depends_on("cuda@9.0:", when="cuda_arch=72")
|
||||||
depends_on("cuda@10.0:", when="cuda_arch=75")
|
depends_on("cuda@10.0:", when="cuda_arch=75")
|
||||||
|
|
||||||
|
# Ampere support:
|
||||||
depends_on("cuda@11.0:", when="cuda_arch=80")
|
depends_on("cuda@11.0:", when="cuda_arch=80")
|
||||||
depends_on("cuda@11.1:", when="cuda_arch=86")
|
depends_on("cuda@11.1:", when="cuda_arch=86")
|
||||||
depends_on("cuda@11.4:", when="cuda_arch=87")
|
depends_on("cuda@11.4:", when="cuda_arch=87")
|
||||||
|
# Ada support:
|
||||||
depends_on("cuda@11.8:", when="cuda_arch=89")
|
depends_on("cuda@11.8:", when="cuda_arch=89")
|
||||||
|
|
||||||
|
# Hopper support:
|
||||||
depends_on("cuda@12.0:", when="cuda_arch=90")
|
depends_on("cuda@12.0:", when="cuda_arch=90")
|
||||||
depends_on("cuda@12.0:", when="cuda_arch=90a")
|
depends_on("cuda@12.0:", when="cuda_arch=90a")
|
||||||
|
|
||||||
|
# Blackwell support:
|
||||||
|
depends_on("cuda@12.8:", when="cuda_arch=100")
|
||||||
|
depends_on("cuda@12.8:", when="cuda_arch=100a")
|
||||||
|
depends_on("cuda@12.8:", when="cuda_arch=101")
|
||||||
|
depends_on("cuda@12.8:", when="cuda_arch=101a")
|
||||||
|
depends_on("cuda@12.8:", when="cuda_arch=120")
|
||||||
|
depends_on("cuda@12.8:", when="cuda_arch=120a")
|
||||||
# From the NVIDIA install guide we know of conflicts for particular
|
# From the NVIDIA install guide we know of conflicts for particular
|
||||||
# platforms (linux, darwin), architectures (x86, powerpc) and compilers
|
# platforms (linux, darwin), architectures (x86, powerpc) and compilers
|
||||||
# (gcc, clang). We don't restrict %gcc and %clang conflicts to
|
# (gcc, clang). We don't restrict %gcc and %clang conflicts to
|
||||||
@@ -163,6 +186,7 @@ def compute_capabilities(arch_list: Iterable[str]) -> List[str]:
|
|||||||
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.6")
|
||||||
|
conflicts("%gcc@15:", when="+cuda ^cuda@:12.8")
|
||||||
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,6 +195,7 @@ def compute_capabilities(arch_list: Iterable[str]) -> List[str]:
|
|||||||
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")
|
conflicts("%clang@19:", when="+cuda ^cuda@:12.6")
|
||||||
|
conflicts("%clang@20:", when="+cuda ^cuda@:12.8")
|
||||||
|
|
||||||
# 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")
|
||||||
|
@@ -7,6 +7,8 @@
|
|||||||
import spack.directives
|
import spack.directives
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
|
|
||||||
from ._checks import BuilderWithDefaults, apply_macos_rpath_fixups, execute_install_time_tests
|
from ._checks import BuilderWithDefaults, apply_macos_rpath_fixups, execute_install_time_tests
|
||||||
|
|
||||||
@@ -48,3 +50,8 @@ class GenericBuilder(BuilderWithDefaults):
|
|||||||
|
|
||||||
# unconditionally perform any post-install phase tests
|
# unconditionally perform any post-install phase tests
|
||||||
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
spack.phase_callbacks.run_after("install")(execute_install_time_tests)
|
||||||
|
|
||||||
|
def install(
|
||||||
|
self, pkg: Package, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
|
raise NotImplementedError
|
||||||
|
@@ -7,7 +7,9 @@
|
|||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
from spack.directives import build_system, extends
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
|
from spack.directives import build_system, depends_on
|
||||||
from spack.multimethod import when
|
from spack.multimethod import when
|
||||||
|
|
||||||
from ._checks import BuilderWithDefaults, execute_install_time_tests
|
from ._checks import BuilderWithDefaults, execute_install_time_tests
|
||||||
@@ -26,9 +28,7 @@ class GoPackage(spack.package_base.PackageBase):
|
|||||||
build_system("go")
|
build_system("go")
|
||||||
|
|
||||||
with when("build_system=go"):
|
with when("build_system=go"):
|
||||||
# TODO: this seems like it should be depends_on, see
|
depends_on("go", type="build")
|
||||||
# setup_dependent_build_environment in go for why I kept it like this
|
|
||||||
extends("go@1.14:", type="build")
|
|
||||||
|
|
||||||
|
|
||||||
@spack.builder.builder("go")
|
@spack.builder.builder("go")
|
||||||
@@ -71,6 +71,7 @@ class GoBuilder(BuilderWithDefaults):
|
|||||||
def setup_build_environment(self, env):
|
def setup_build_environment(self, env):
|
||||||
env.set("GO111MODULE", "on")
|
env.set("GO111MODULE", "on")
|
||||||
env.set("GOTOOLCHAIN", "local")
|
env.set("GOTOOLCHAIN", "local")
|
||||||
|
env.set("GOPATH", fs.join_path(self.pkg.stage.path, "go"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def build_directory(self):
|
def build_directory(self):
|
||||||
@@ -81,19 +82,31 @@ def build_directory(self):
|
|||||||
def build_args(self):
|
def build_args(self):
|
||||||
"""Arguments for ``go build``."""
|
"""Arguments for ``go build``."""
|
||||||
# Pass ldflags -s = --strip-all and -w = --no-warnings by default
|
# Pass ldflags -s = --strip-all and -w = --no-warnings by default
|
||||||
return ["-modcacherw", "-ldflags", "-s -w", "-o", f"{self.pkg.name}"]
|
return [
|
||||||
|
"-p",
|
||||||
|
str(self.pkg.module.make_jobs),
|
||||||
|
"-modcacherw",
|
||||||
|
"-ldflags",
|
||||||
|
"-s -w",
|
||||||
|
"-o",
|
||||||
|
f"{self.pkg.name}",
|
||||||
|
]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def check_args(self):
|
def check_args(self):
|
||||||
"""Argument for ``go test`` during check phase"""
|
"""Argument for ``go test`` during check phase"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: GoPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""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)
|
pkg.module.go("build", *self.build_args)
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: GoPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Install built binaries into prefix bin."""
|
"""Install built binaries into prefix bin."""
|
||||||
with fs.working_dir(self.build_directory):
|
with fs.working_dir(self.build_directory):
|
||||||
fs.mkdirp(prefix.bin)
|
fs.mkdirp(prefix.bin)
|
||||||
|
@@ -7,7 +7,9 @@
|
|||||||
|
|
||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
|
import spack.spec
|
||||||
import spack.util.executable
|
import spack.util.executable
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on, extends
|
from spack.directives import build_system, depends_on, extends
|
||||||
from spack.multimethod import when
|
from spack.multimethod import when
|
||||||
|
|
||||||
@@ -55,7 +57,9 @@ class LuaBuilder(spack.builder.Builder):
|
|||||||
#: Names associated with package attributes in the old build-system format
|
#: Names associated with package attributes in the old build-system format
|
||||||
legacy_attributes = ()
|
legacy_attributes = ()
|
||||||
|
|
||||||
def unpack(self, pkg, spec, prefix):
|
def unpack(
|
||||||
|
self, pkg: LuaPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
if os.path.splitext(pkg.stage.archive_file)[1] == ".rock":
|
if os.path.splitext(pkg.stage.archive_file)[1] == ".rock":
|
||||||
directory = pkg.luarocks("unpack", pkg.stage.archive_file, output=str)
|
directory = pkg.luarocks("unpack", pkg.stage.archive_file, output=str)
|
||||||
dirlines = directory.split("\n")
|
dirlines = directory.split("\n")
|
||||||
@@ -66,15 +70,16 @@ def unpack(self, pkg, spec, prefix):
|
|||||||
def _generate_tree_line(name, prefix):
|
def _generate_tree_line(name, prefix):
|
||||||
return """{{ name = "{name}", root = "{prefix}" }};""".format(name=name, prefix=prefix)
|
return """{{ name = "{name}", root = "{prefix}" }};""".format(name=name, prefix=prefix)
|
||||||
|
|
||||||
def generate_luarocks_config(self, pkg, spec, prefix):
|
def generate_luarocks_config(
|
||||||
|
self, pkg: LuaPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
spec = self.pkg.spec
|
spec = self.pkg.spec
|
||||||
table_entries = []
|
table_entries = []
|
||||||
for d in spec.traverse(deptype=("build", "run")):
|
for d in spec.traverse(deptype=("build", "run")):
|
||||||
if d.package.extends(self.pkg.extendee_spec):
|
if d.package.extends(self.pkg.extendee_spec):
|
||||||
table_entries.append(self._generate_tree_line(d.name, d.prefix))
|
table_entries.append(self._generate_tree_line(d.name, d.prefix))
|
||||||
|
|
||||||
path = self._luarocks_config_path()
|
with open(self._luarocks_config_path(), "w", encoding="utf-8") as config:
|
||||||
with open(path, "w", encoding="utf-8") as config:
|
|
||||||
config.write(
|
config.write(
|
||||||
"""
|
"""
|
||||||
deps_mode="all"
|
deps_mode="all"
|
||||||
@@ -85,23 +90,26 @@ def generate_luarocks_config(self, pkg, spec, prefix):
|
|||||||
"\n".join(table_entries)
|
"\n".join(table_entries)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return path
|
|
||||||
|
|
||||||
def preprocess(self, pkg, spec, prefix):
|
def preprocess(
|
||||||
|
self, pkg: LuaPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Override this to preprocess source before building with luarocks"""
|
"""Override this to preprocess source before building with luarocks"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def luarocks_args(self):
|
def luarocks_args(self):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: LuaPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
rock = "."
|
rock = "."
|
||||||
specs = find(".", "*.rockspec", recursive=False)
|
specs = find(".", "*.rockspec", recursive=False)
|
||||||
if specs:
|
if specs:
|
||||||
rock = specs[0]
|
rock = specs[0]
|
||||||
rocks_args = self.luarocks_args()
|
rocks_args = self.luarocks_args()
|
||||||
rocks_args.append(rock)
|
rocks_args.append(rock)
|
||||||
self.pkg.luarocks("--tree=" + prefix, "make", *rocks_args)
|
pkg.luarocks("--tree=" + prefix, "make", *rocks_args)
|
||||||
|
|
||||||
def _luarocks_config_path(self):
|
def _luarocks_config_path(self):
|
||||||
return os.path.join(self.pkg.stage.source_path, "spack_luarocks.lua")
|
return os.path.join(self.pkg.stage.source_path, "spack_luarocks.lua")
|
||||||
|
@@ -98,29 +98,20 @@ def build_directory(self) -> str:
|
|||||||
return self.pkg.stage.source_path
|
return self.pkg.stage.source_path
|
||||||
|
|
||||||
def edit(
|
def edit(
|
||||||
self,
|
self, pkg: MakefilePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Edit the Makefile before calling make. The default is a no-op."""
|
"""Edit the Makefile before calling make. The default is a no-op."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
self,
|
self, pkg: MakefilePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""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)
|
pkg.module.make(*self.build_targets)
|
||||||
|
|
||||||
def install(
|
def install(
|
||||||
self,
|
self, pkg: MakefilePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""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):
|
||||||
|
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on
|
from spack.directives import build_system, depends_on
|
||||||
from spack.multimethod import when
|
from spack.multimethod import when
|
||||||
from spack.util.executable import which
|
from spack.util.executable import which
|
||||||
@@ -58,16 +60,20 @@ def build_args(self):
|
|||||||
"""List of args to pass to build phase."""
|
"""List of args to pass to build phase."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: MavenPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Compile code and package into a JAR file."""
|
"""Compile code and package into a JAR file."""
|
||||||
with fs.working_dir(self.build_directory):
|
with fs.working_dir(self.build_directory):
|
||||||
mvn = which("mvn")
|
mvn = which("mvn", required=True)
|
||||||
if self.pkg.run_tests:
|
if self.pkg.run_tests:
|
||||||
mvn("verify", *self.build_args())
|
mvn("verify", *self.build_args())
|
||||||
else:
|
else:
|
||||||
mvn("package", "-DskipTests", *self.build_args())
|
mvn("package", "-DskipTests", *self.build_args())
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: MavenPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Copy to installation prefix."""
|
"""Copy to installation prefix."""
|
||||||
with fs.working_dir(self.build_directory):
|
with fs.working_dir(self.build_directory):
|
||||||
fs.install_tree(".", prefix)
|
fs.install_tree(".", prefix)
|
||||||
|
@@ -188,10 +188,7 @@ def meson_args(self) -> List[str]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def meson(
|
def meson(
|
||||||
self,
|
self, pkg: MesonPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run ``meson`` in the build directory"""
|
"""Run ``meson`` in the build directory"""
|
||||||
options = []
|
options = []
|
||||||
@@ -204,10 +201,7 @@ def meson(
|
|||||||
pkg.module.meson(*options)
|
pkg.module.meson(*options)
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
self,
|
self, pkg: MesonPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Make the build targets"""
|
"""Make the build targets"""
|
||||||
options = ["-v"]
|
options = ["-v"]
|
||||||
@@ -216,10 +210,7 @@ def build(
|
|||||||
pkg.module.ninja(*options)
|
pkg.module.ninja(*options)
|
||||||
|
|
||||||
def install(
|
def install(
|
||||||
self,
|
self, pkg: MesonPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
pkg: spack.package_base.PackageBase,
|
|
||||||
spec: spack.spec.Spec,
|
|
||||||
prefix: spack.util.prefix.Prefix,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Make the install targets"""
|
"""Make the install targets"""
|
||||||
with fs.working_dir(self.build_directory):
|
with fs.working_dir(self.build_directory):
|
||||||
|
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, conflicts
|
from spack.directives import build_system, conflicts
|
||||||
|
|
||||||
from ._checks import BuilderWithDefaults
|
from ._checks import BuilderWithDefaults
|
||||||
@@ -99,7 +101,9 @@ def msbuild_install_args(self):
|
|||||||
as `msbuild_args` by default."""
|
as `msbuild_args` by default."""
|
||||||
return self.msbuild_args()
|
return self.msbuild_args()
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: MSBuildPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""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(
|
pkg.module.msbuild(
|
||||||
@@ -108,7 +112,9 @@ def build(self, pkg, spec, prefix):
|
|||||||
self.define_targets(*self.build_targets),
|
self.define_targets(*self.build_targets),
|
||||||
)
|
)
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: MSBuildPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""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):
|
||||||
|
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, conflicts
|
from spack.directives import build_system, conflicts
|
||||||
|
|
||||||
from ._checks import BuilderWithDefaults
|
from ._checks import BuilderWithDefaults
|
||||||
@@ -123,7 +125,9 @@ def nmake_install_args(self):
|
|||||||
Individual packages should override to specify NMake args to command line"""
|
Individual packages should override to specify NMake args to command line"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: NMakePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Run "nmake" on the build targets specified by the builder."""
|
"""Run "nmake" on the build targets specified by the builder."""
|
||||||
opts = self.std_nmake_args
|
opts = self.std_nmake_args
|
||||||
opts += self.nmake_args()
|
opts += self.nmake_args()
|
||||||
@@ -132,7 +136,9 @@ def build(self, pkg, spec, prefix):
|
|||||||
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)
|
pkg.module.nmake(*opts, *self.build_targets, ignore_quotes=self.ignore_quotes)
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: NMakePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Run "nmake" on the install targets specified by the builder.
|
"""Run "nmake" on the install targets specified by the builder.
|
||||||
This is INSTALL by default"""
|
This is INSTALL by default"""
|
||||||
opts = self.std_nmake_args
|
opts = self.std_nmake_args
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, extends
|
from spack.directives import build_system, extends
|
||||||
from spack.multimethod import when
|
from spack.multimethod import when
|
||||||
|
|
||||||
@@ -42,7 +44,9 @@ class OctaveBuilder(BuilderWithDefaults):
|
|||||||
#: Names associated with package attributes in the old build-system format
|
#: Names associated with package attributes in the old build-system format
|
||||||
legacy_attributes = ()
|
legacy_attributes = ()
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: OctavePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Install the package from the archive file"""
|
"""Install the package from the archive file"""
|
||||||
pkg.module.octave(
|
pkg.module.octave(
|
||||||
"--quiet",
|
"--quiet",
|
||||||
|
@@ -10,6 +10,8 @@
|
|||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on, extends
|
from spack.directives import build_system, depends_on, extends
|
||||||
from spack.install_test import SkipTest, test_part
|
from spack.install_test import SkipTest, test_part
|
||||||
from spack.multimethod import when
|
from spack.multimethod import when
|
||||||
@@ -149,7 +151,9 @@ def configure_args(self):
|
|||||||
"""
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def configure(self, pkg, spec, prefix):
|
def configure(
|
||||||
|
self, pkg: PerlPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Run Makefile.PL or Build.PL with arguments consisting of
|
"""Run Makefile.PL or Build.PL with arguments consisting of
|
||||||
an appropriate installation base directory followed by the
|
an appropriate installation base directory followed by the
|
||||||
list returned by :py:meth:`~.PerlBuilder.configure_args`.
|
list returned by :py:meth:`~.PerlBuilder.configure_args`.
|
||||||
@@ -173,7 +177,9 @@ def fix_shebang(self):
|
|||||||
repl = "#!/usr/bin/env perl"
|
repl = "#!/usr/bin/env perl"
|
||||||
filter_file(pattern, repl, "Build", backup=False)
|
filter_file(pattern, repl, "Build", backup=False)
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: PerlPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Builds a Perl package."""
|
"""Builds a Perl package."""
|
||||||
self.build_executable()
|
self.build_executable()
|
||||||
|
|
||||||
@@ -184,6 +190,8 @@ def check(self):
|
|||||||
"""Runs built-in tests of a Perl package."""
|
"""Runs built-in tests of a Perl package."""
|
||||||
self.build_executable("test")
|
self.build_executable("test")
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: PerlPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Installs a Perl package."""
|
"""Installs a Perl package."""
|
||||||
self.build_executable("install")
|
self.build_executable("install")
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
import spack.repo
|
import spack.repo
|
||||||
import spack.spec
|
import spack.spec
|
||||||
import spack.store
|
import spack.store
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on, extends
|
from spack.directives import build_system, depends_on, extends
|
||||||
from spack.error import NoHeadersError, NoLibrariesError
|
from spack.error import NoHeadersError, NoLibrariesError
|
||||||
from spack.install_test import test_part
|
from spack.install_test import test_part
|
||||||
@@ -263,16 +264,17 @@ def update_external_dependencies(self, extendee_spec=None):
|
|||||||
# Ensure architecture information is present
|
# Ensure architecture information is present
|
||||||
if not python.architecture:
|
if not python.architecture:
|
||||||
host_platform = spack.platforms.host()
|
host_platform = spack.platforms.host()
|
||||||
host_os = host_platform.operating_system("default_os")
|
host_os = host_platform.default_operating_system()
|
||||||
host_target = host_platform.target("default_target")
|
host_target = host_platform.default_target()
|
||||||
python.architecture = spack.spec.ArchSpec(
|
python.architecture = spack.spec.ArchSpec(
|
||||||
(str(host_platform), str(host_os), str(host_target))
|
(str(host_platform), str(host_os), str(host_target))
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if not python.architecture.platform:
|
if not python.architecture.platform:
|
||||||
python.architecture.platform = spack.platforms.host()
|
python.architecture.platform = spack.platforms.host()
|
||||||
|
platform = spack.platforms.by_name(python.architecture.platform)
|
||||||
if not python.architecture.os:
|
if not python.architecture.os:
|
||||||
python.architecture.os = "default_os"
|
python.architecture.os = platform.default_operating_system()
|
||||||
if not python.architecture.target:
|
if not python.architecture.target:
|
||||||
python.architecture.target = archspec.cpu.host().family.name
|
python.architecture.target = archspec.cpu.host().family.name
|
||||||
|
|
||||||
|
@@ -6,6 +6,8 @@
|
|||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on
|
from spack.directives import build_system, depends_on
|
||||||
|
|
||||||
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
||||||
@@ -27,6 +29,7 @@ class QMakePackage(spack.package_base.PackageBase):
|
|||||||
build_system("qmake")
|
build_system("qmake")
|
||||||
|
|
||||||
depends_on("qmake", type="build", when="build_system=qmake")
|
depends_on("qmake", type="build", when="build_system=qmake")
|
||||||
|
depends_on("gmake", type="build")
|
||||||
|
|
||||||
|
|
||||||
@spack.builder.builder("qmake")
|
@spack.builder.builder("qmake")
|
||||||
@@ -61,17 +64,23 @@ def qmake_args(self):
|
|||||||
"""List of arguments passed to qmake."""
|
"""List of arguments passed to qmake."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def qmake(self, pkg, spec, prefix):
|
def qmake(
|
||||||
|
self, pkg: QMakePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""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())
|
pkg.module.qmake(*self.qmake_args())
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: QMakePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Make the build targets"""
|
"""Make the build targets"""
|
||||||
with working_dir(self.build_directory):
|
with working_dir(self.build_directory):
|
||||||
pkg.module.make()
|
pkg.module.make()
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: QMakePackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Make the install targets"""
|
"""Make the install targets"""
|
||||||
with working_dir(self.build_directory):
|
with working_dir(self.build_directory):
|
||||||
pkg.module.make("install")
|
pkg.module.make("install")
|
||||||
|
@@ -94,7 +94,7 @@ def list_url(cls):
|
|||||||
if cls.cran:
|
if cls.cran:
|
||||||
return f"https://cloud.r-project.org/src/contrib/Archive/{cls.cran}/"
|
return f"https://cloud.r-project.org/src/contrib/Archive/{cls.cran}/"
|
||||||
|
|
||||||
@property
|
@lang.classproperty
|
||||||
def git(self):
|
def git(cls):
|
||||||
if self.bioc:
|
if cls.bioc:
|
||||||
return f"https://git.bioconductor.org/packages/{self.bioc}"
|
return f"https://git.bioconductor.org/packages/{cls.bioc}"
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
|
||||||
import spack.builder
|
import spack.builder
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
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.config import determine_number_of_jobs
|
||||||
from spack.directives import build_system, extends, maintainers
|
from spack.directives import build_system, extends, maintainers
|
||||||
@@ -74,18 +76,22 @@ def build_directory(self):
|
|||||||
ret = os.path.join(ret, self.subdirectory)
|
ret = os.path.join(ret, self.subdirectory)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: RacketPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Install everything from build directory."""
|
"""Install everything from build directory."""
|
||||||
raco = Executable("raco")
|
raco = Executable("raco")
|
||||||
with fs.working_dir(self.build_directory):
|
with fs.working_dir(self.build_directory):
|
||||||
parallel = self.pkg.parallel and (not env_flag(SPACK_NO_PARALLEL_MAKE))
|
parallel = pkg.parallel and (not env_flag(SPACK_NO_PARALLEL_MAKE))
|
||||||
|
name = pkg.racket_name
|
||||||
|
assert name is not None, "Racket package name is not set"
|
||||||
args = [
|
args = [
|
||||||
"pkg",
|
"pkg",
|
||||||
"install",
|
"install",
|
||||||
"-t",
|
"-t",
|
||||||
"dir",
|
"dir",
|
||||||
"-n",
|
"-n",
|
||||||
self.pkg.racket_name,
|
name,
|
||||||
"--deps",
|
"--deps",
|
||||||
"fail",
|
"fail",
|
||||||
"--ignore-implies",
|
"--ignore-implies",
|
||||||
@@ -101,8 +107,7 @@ def install(self, pkg, spec, prefix):
|
|||||||
except ProcessError:
|
except ProcessError:
|
||||||
args.insert(-2, "--skip-installed")
|
args.insert(-2, "--skip-installed")
|
||||||
raco(*args)
|
raco(*args)
|
||||||
msg = (
|
tty.warn(
|
||||||
"Racket package {0} was already installed, uninstalling via "
|
f"Racket package {name} was already installed, uninstalling via "
|
||||||
"Spack may make someone unhappy!"
|
"Spack may make someone unhappy!"
|
||||||
)
|
)
|
||||||
tty.warn(msg.format(self.pkg.racket_name))
|
|
||||||
|
@@ -140,7 +140,7 @@ class ROCmPackage(PackageBase):
|
|||||||
when="+rocm",
|
when="+rocm",
|
||||||
)
|
)
|
||||||
|
|
||||||
depends_on("llvm-amdgpu", when="+rocm")
|
depends_on("llvm-amdgpu", type="build", when="+rocm")
|
||||||
depends_on("hsa-rocr-dev", when="+rocm")
|
depends_on("hsa-rocr-dev", when="+rocm")
|
||||||
depends_on("hip +rocm", when="+rocm")
|
depends_on("hip +rocm", when="+rocm")
|
||||||
|
|
||||||
|
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, extends, maintainers
|
from spack.directives import build_system, extends, maintainers
|
||||||
|
|
||||||
from ._checks import BuilderWithDefaults
|
from ._checks import BuilderWithDefaults
|
||||||
@@ -42,7 +44,9 @@ class RubyBuilder(BuilderWithDefaults):
|
|||||||
#: Names associated with package attributes in the old build-system format
|
#: Names associated with package attributes in the old build-system format
|
||||||
legacy_attributes = ()
|
legacy_attributes = ()
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: RubyPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Build a Ruby gem."""
|
"""Build a Ruby gem."""
|
||||||
|
|
||||||
# ruby-rake provides both rake.gemspec and Rakefile, but only
|
# ruby-rake provides both rake.gemspec and Rakefile, but only
|
||||||
@@ -58,7 +62,9 @@ def build(self, pkg, spec, prefix):
|
|||||||
# Some Ruby packages only ship `*.gem` files, so nothing to build
|
# Some Ruby packages only ship `*.gem` files, so nothing to build
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: RubyPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Install a Ruby gem.
|
"""Install a Ruby gem.
|
||||||
|
|
||||||
The ruby package sets ``GEM_HOME`` to tell gem where to install to."""
|
The ruby package sets ``GEM_HOME`` to tell gem where to install to."""
|
||||||
|
@@ -4,6 +4,8 @@
|
|||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on
|
from spack.directives import build_system, depends_on
|
||||||
|
|
||||||
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
from ._checks import BuilderWithDefaults, execute_build_time_tests
|
||||||
@@ -59,7 +61,9 @@ def build_args(self, spec, prefix):
|
|||||||
"""Arguments to pass to build."""
|
"""Arguments to pass to build."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: SConsPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Build the package."""
|
"""Build the package."""
|
||||||
pkg.module.scons(*self.build_args(spec, prefix))
|
pkg.module.scons(*self.build_args(spec, prefix))
|
||||||
|
|
||||||
@@ -67,7 +71,9 @@ def install_args(self, spec, prefix):
|
|||||||
"""Arguments to pass to install."""
|
"""Arguments to pass to install."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: SConsPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Install the package."""
|
"""Install the package."""
|
||||||
pkg.module.scons("install", *self.install_args(spec, prefix))
|
pkg.module.scons("install", *self.install_args(spec, prefix))
|
||||||
|
|
||||||
|
@@ -11,6 +11,8 @@
|
|||||||
import spack.install_test
|
import spack.install_test
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on, extends
|
from spack.directives import build_system, depends_on, extends
|
||||||
from spack.multimethod import when
|
from spack.multimethod import when
|
||||||
from spack.util.executable import Executable
|
from spack.util.executable import Executable
|
||||||
@@ -41,6 +43,7 @@ class SIPPackage(spack.package_base.PackageBase):
|
|||||||
with when("build_system=sip"):
|
with when("build_system=sip"):
|
||||||
extends("python", type=("build", "link", "run"))
|
extends("python", type=("build", "link", "run"))
|
||||||
depends_on("py-sip", type="build")
|
depends_on("py-sip", type="build")
|
||||||
|
depends_on("gmake", type="build")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def import_modules(self):
|
def import_modules(self):
|
||||||
@@ -130,7 +133,9 @@ class SIPBuilder(BuilderWithDefaults):
|
|||||||
|
|
||||||
build_directory = "build"
|
build_directory = "build"
|
||||||
|
|
||||||
def configure(self, pkg, spec, prefix):
|
def configure(
|
||||||
|
self, pkg: SIPPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""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
|
||||||
@@ -148,7 +153,9 @@ def configure_args(self):
|
|||||||
"""Arguments to pass to configure."""
|
"""Arguments to pass to configure."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: SIPPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Build the package."""
|
"""Build the package."""
|
||||||
args = self.build_args()
|
args = self.build_args()
|
||||||
|
|
||||||
@@ -159,7 +166,9 @@ def build_args(self):
|
|||||||
"""Arguments to pass to build."""
|
"""Arguments to pass to build."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: SIPPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Install the package."""
|
"""Install the package."""
|
||||||
args = self.install_args()
|
args = self.install_args()
|
||||||
|
|
||||||
|
@@ -6,6 +6,8 @@
|
|||||||
import spack.builder
|
import spack.builder
|
||||||
import spack.package_base
|
import spack.package_base
|
||||||
import spack.phase_callbacks
|
import spack.phase_callbacks
|
||||||
|
import spack.spec
|
||||||
|
import spack.util.prefix
|
||||||
from spack.directives import build_system, depends_on
|
from spack.directives import build_system, depends_on
|
||||||
|
|
||||||
from ._checks import BuilderWithDefaults, execute_build_time_tests, execute_install_time_tests
|
from ._checks import BuilderWithDefaults, execute_build_time_tests, execute_install_time_tests
|
||||||
@@ -97,7 +99,9 @@ def waf(self, *args, **kwargs):
|
|||||||
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)
|
||||||
|
|
||||||
def configure(self, pkg, spec, prefix):
|
def configure(
|
||||||
|
self, pkg: WafPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Configures the project."""
|
"""Configures the project."""
|
||||||
args = ["--prefix={0}".format(self.pkg.prefix)]
|
args = ["--prefix={0}".format(self.pkg.prefix)]
|
||||||
args += self.configure_args()
|
args += self.configure_args()
|
||||||
@@ -108,7 +112,9 @@ def configure_args(self):
|
|||||||
"""Arguments to pass to configure."""
|
"""Arguments to pass to configure."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def build(self, pkg, spec, prefix):
|
def build(
|
||||||
|
self, pkg: WafPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Executes the build."""
|
"""Executes the build."""
|
||||||
args = self.build_args()
|
args = self.build_args()
|
||||||
|
|
||||||
@@ -118,7 +124,9 @@ def build_args(self):
|
|||||||
"""Arguments to pass to build."""
|
"""Arguments to pass to build."""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def install(self, pkg, spec, prefix):
|
def install(
|
||||||
|
self, pkg: WafPackage, spec: spack.spec.Spec, prefix: spack.util.prefix.Prefix
|
||||||
|
) -> None:
|
||||||
"""Installs the targets on the system."""
|
"""Installs the targets on the system."""
|
||||||
args = self.install_args()
|
args = self.install_args()
|
||||||
|
|
||||||
|
@@ -14,9 +14,9 @@
|
|||||||
import zipfile
|
import zipfile
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import Callable, Dict, List, Set
|
from typing import Callable, Dict, List, Set
|
||||||
from urllib.error import HTTPError, URLError
|
from urllib.request import Request
|
||||||
from urllib.request import HTTPHandler, Request, build_opener
|
|
||||||
|
|
||||||
|
import llnl.path
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.tty.color import cescape, colorize
|
from llnl.util.tty.color import cescape, colorize
|
||||||
@@ -63,6 +63,8 @@
|
|||||||
|
|
||||||
PushResult = namedtuple("PushResult", "success url")
|
PushResult = namedtuple("PushResult", "success url")
|
||||||
|
|
||||||
|
urlopen = web_util.urlopen # alias for mocking in tests
|
||||||
|
|
||||||
|
|
||||||
def get_change_revisions():
|
def get_change_revisions():
|
||||||
"""If this is a git repo get the revisions to use when checking
|
"""If this is a git repo get the revisions to use when checking
|
||||||
@@ -82,6 +84,9 @@ def get_stack_changed(env_path, rev1="HEAD^", rev2="HEAD"):
|
|||||||
whether or not the stack was changed. Returns True if the environment
|
whether or not the stack was changed. Returns True if the environment
|
||||||
manifest changed between the provided revisions (or additionally if the
|
manifest changed between the provided revisions (or additionally if the
|
||||||
`.gitlab-ci.yml` file itself changed). Returns False otherwise."""
|
`.gitlab-ci.yml` file itself changed). Returns False otherwise."""
|
||||||
|
# git returns posix paths always, normalize input to be comptaible
|
||||||
|
# with that
|
||||||
|
env_path = llnl.path.convert_to_posix_path(env_path)
|
||||||
git = spack.util.git.git()
|
git = spack.util.git.git()
|
||||||
if git:
|
if git:
|
||||||
with fs.working_dir(spack.paths.prefix):
|
with fs.working_dir(spack.paths.prefix):
|
||||||
@@ -472,12 +477,9 @@ def generate_pipeline(env: ev.Environment, args) -> None:
|
|||||||
# Use all unpruned specs to populate the build group for this set
|
# Use all unpruned specs to populate the build group for this set
|
||||||
cdash_config = cfg.get("cdash")
|
cdash_config = cfg.get("cdash")
|
||||||
if options.cdash_handler and options.cdash_handler.auth_token:
|
if options.cdash_handler and options.cdash_handler.auth_token:
|
||||||
try:
|
options.cdash_handler.populate_buildgroup(
|
||||||
options.cdash_handler.populate_buildgroup(
|
[options.cdash_handler.build_name(s) for s in pipeline_specs]
|
||||||
[options.cdash_handler.build_name(s) for s in pipeline_specs]
|
)
|
||||||
)
|
|
||||||
except (SpackError, HTTPError, URLError, TimeoutError) as err:
|
|
||||||
tty.warn(f"Problem populating buildgroup: {err}")
|
|
||||||
elif cdash_config:
|
elif cdash_config:
|
||||||
# warn only if there was actually a CDash configuration.
|
# 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")
|
||||||
@@ -631,29 +633,19 @@ def download_and_extract_artifacts(url, work_dir):
|
|||||||
if token:
|
if token:
|
||||||
headers["PRIVATE-TOKEN"] = token
|
headers["PRIVATE-TOKEN"] = token
|
||||||
|
|
||||||
opener = build_opener(HTTPHandler)
|
request = Request(url, headers=headers, method="GET")
|
||||||
|
|
||||||
request = Request(url, headers=headers)
|
|
||||||
request.get_method = lambda: "GET"
|
|
||||||
|
|
||||||
response = opener.open(request, timeout=SPACK_CDASH_TIMEOUT)
|
|
||||||
response_code = response.getcode()
|
|
||||||
|
|
||||||
if response_code != 200:
|
|
||||||
msg = f"Error response code ({response_code}) in reproduce_ci_job"
|
|
||||||
raise SpackError(msg)
|
|
||||||
|
|
||||||
artifacts_zip_path = os.path.join(work_dir, "artifacts.zip")
|
artifacts_zip_path = os.path.join(work_dir, "artifacts.zip")
|
||||||
|
os.makedirs(work_dir, exist_ok=True)
|
||||||
|
|
||||||
if not os.path.exists(work_dir):
|
try:
|
||||||
os.makedirs(work_dir)
|
response = urlopen(request, timeout=SPACK_CDASH_TIMEOUT)
|
||||||
|
with open(artifacts_zip_path, "wb") as out_file:
|
||||||
|
shutil.copyfileobj(response, out_file)
|
||||||
|
except OSError as e:
|
||||||
|
raise SpackError(f"Error fetching artifacts: {e}")
|
||||||
|
|
||||||
with open(artifacts_zip_path, "wb") as out_file:
|
with zipfile.ZipFile(artifacts_zip_path) as zip_file:
|
||||||
shutil.copyfileobj(response, out_file)
|
zip_file.extractall(work_dir)
|
||||||
|
|
||||||
zip_file = zipfile.ZipFile(artifacts_zip_path)
|
|
||||||
zip_file.extractall(work_dir)
|
|
||||||
zip_file.close()
|
|
||||||
|
|
||||||
os.remove(artifacts_zip_path)
|
os.remove(artifacts_zip_path)
|
||||||
|
|
||||||
|
@@ -1,23 +1,21 @@
|
|||||||
# Copyright Spack Project Developers. See COPYRIGHT file for details.
|
# Copyright Spack Project Developers. See COPYRIGHT file for details.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
import codecs
|
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import ssl
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, Generator, List, Optional, Set, Tuple
|
from typing import Dict, Generator, List, Optional, Set, Tuple
|
||||||
from urllib.parse import quote, urlencode, urlparse
|
from urllib.parse import quote, urlencode, urlparse
|
||||||
from urllib.request import HTTPHandler, HTTPSHandler, Request, build_opener
|
from urllib.request import Request
|
||||||
|
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.lang import Singleton, memoized
|
from llnl.util.lang import memoized
|
||||||
|
|
||||||
import spack.binary_distribution as bindist
|
import spack.binary_distribution as bindist
|
||||||
import spack.config as cfg
|
import spack.config as cfg
|
||||||
@@ -35,32 +33,11 @@
|
|||||||
from spack.reporters.cdash import SPACK_CDASH_TIMEOUT
|
from spack.reporters.cdash import SPACK_CDASH_TIMEOUT
|
||||||
from spack.reporters.cdash import build_stamp as cdash_build_stamp
|
from spack.reporters.cdash import build_stamp as cdash_build_stamp
|
||||||
|
|
||||||
|
|
||||||
def _urlopen():
|
|
||||||
error_handler = web_util.SpackHTTPDefaultErrorHandler()
|
|
||||||
|
|
||||||
# One opener with HTTPS ssl enabled
|
|
||||||
with_ssl = build_opener(
|
|
||||||
HTTPHandler(), HTTPSHandler(context=web_util.ssl_create_default_context()), error_handler
|
|
||||||
)
|
|
||||||
|
|
||||||
# One opener with HTTPS ssl disabled
|
|
||||||
without_ssl = build_opener(
|
|
||||||
HTTPHandler(), HTTPSHandler(context=ssl._create_unverified_context()), error_handler
|
|
||||||
)
|
|
||||||
|
|
||||||
# And dynamically dispatch based on the config:verify_ssl.
|
|
||||||
def dispatch_open(fullurl, data=None, timeout=None, verify_ssl=True):
|
|
||||||
opener = with_ssl if verify_ssl else without_ssl
|
|
||||||
timeout = timeout or cfg.get("config:connect_timeout", 1)
|
|
||||||
return opener.open(fullurl, data, timeout)
|
|
||||||
|
|
||||||
return dispatch_open
|
|
||||||
|
|
||||||
|
|
||||||
IS_WINDOWS = sys.platform == "win32"
|
IS_WINDOWS = sys.platform == "win32"
|
||||||
SPACK_RESERVED_TAGS = ["public", "protected", "notary"]
|
SPACK_RESERVED_TAGS = ["public", "protected", "notary"]
|
||||||
_dyn_mapping_urlopener = Singleton(_urlopen)
|
|
||||||
|
# this exists purely for testing purposes
|
||||||
|
_urlopen = web_util.urlopen
|
||||||
|
|
||||||
|
|
||||||
def copy_files_to_artifacts(src, artifacts_dir):
|
def copy_files_to_artifacts(src, artifacts_dir):
|
||||||
@@ -279,26 +256,25 @@ def copy_test_results(self, source, dest):
|
|||||||
reports = fs.join_path(source, "*_Test*.xml")
|
reports = fs.join_path(source, "*_Test*.xml")
|
||||||
copy_files_to_artifacts(reports, dest)
|
copy_files_to_artifacts(reports, dest)
|
||||||
|
|
||||||
def create_buildgroup(self, opener, headers, url, group_name, group_type):
|
def create_buildgroup(self, headers, url, group_name, group_type):
|
||||||
data = {"newbuildgroup": group_name, "project": self.project, "type": group_type}
|
data = {"newbuildgroup": group_name, "project": self.project, "type": group_type}
|
||||||
|
|
||||||
enc_data = json.dumps(data).encode("utf-8")
|
enc_data = json.dumps(data).encode("utf-8")
|
||||||
|
|
||||||
request = Request(url, data=enc_data, headers=headers)
|
request = Request(url, data=enc_data, headers=headers)
|
||||||
|
|
||||||
response = opener.open(request, timeout=SPACK_CDASH_TIMEOUT)
|
try:
|
||||||
response_code = response.getcode()
|
response_text = _urlopen(request, timeout=SPACK_CDASH_TIMEOUT).read()
|
||||||
|
except OSError as e:
|
||||||
if response_code not in [200, 201]:
|
tty.warn(f"Failed to create CDash buildgroup: {e}")
|
||||||
msg = f"Creating buildgroup failed (response code = {response_code})"
|
|
||||||
tty.warn(msg)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
response_text = response.read()
|
try:
|
||||||
response_json = json.loads(response_text)
|
response_json = json.loads(response_text)
|
||||||
build_group_id = response_json["id"]
|
return response_json["id"]
|
||||||
|
except (json.JSONDecodeError, KeyError) as e:
|
||||||
return build_group_id
|
tty.warn(f"Failed to parse CDash response: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
def populate_buildgroup(self, job_names):
|
def populate_buildgroup(self, job_names):
|
||||||
url = f"{self.url}/api/v1/buildgroup.php"
|
url = f"{self.url}/api/v1/buildgroup.php"
|
||||||
@@ -308,16 +284,11 @@ def populate_buildgroup(self, job_names):
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
opener = build_opener(HTTPHandler)
|
parent_group_id = self.create_buildgroup(headers, url, self.build_group, "Daily")
|
||||||
|
group_id = self.create_buildgroup(headers, url, f"Latest {self.build_group}", "Latest")
|
||||||
parent_group_id = self.create_buildgroup(opener, headers, url, self.build_group, "Daily")
|
|
||||||
group_id = self.create_buildgroup(
|
|
||||||
opener, headers, url, f"Latest {self.build_group}", "Latest"
|
|
||||||
)
|
|
||||||
|
|
||||||
if not parent_group_id or not group_id:
|
if not parent_group_id or not group_id:
|
||||||
msg = f"Failed to create or retrieve buildgroups for {self.build_group}"
|
tty.warn(f"Failed to create or retrieve buildgroups for {self.build_group}")
|
||||||
tty.warn(msg)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@@ -329,15 +300,12 @@ def populate_buildgroup(self, job_names):
|
|||||||
|
|
||||||
enc_data = json.dumps(data).encode("utf-8")
|
enc_data = json.dumps(data).encode("utf-8")
|
||||||
|
|
||||||
request = Request(url, data=enc_data, headers=headers)
|
request = Request(url, data=enc_data, headers=headers, method="PUT")
|
||||||
request.get_method = lambda: "PUT"
|
|
||||||
|
|
||||||
response = opener.open(request, timeout=SPACK_CDASH_TIMEOUT)
|
try:
|
||||||
response_code = response.getcode()
|
_urlopen(request, timeout=SPACK_CDASH_TIMEOUT)
|
||||||
|
except OSError as e:
|
||||||
if response_code != 200:
|
tty.warn(f"Failed to populate CDash buildgroup: {e}")
|
||||||
msg = f"Error response code ({response_code}) in populate_buildgroup"
|
|
||||||
tty.warn(msg)
|
|
||||||
|
|
||||||
def report_skipped(self, spec: spack.spec.Spec, report_dir: str, reason: Optional[str]):
|
def report_skipped(self, spec: spack.spec.Spec, report_dir: str, reason: Optional[str]):
|
||||||
"""Explicitly report skipping testing of a spec (e.g., it's CI
|
"""Explicitly report skipping testing of a spec (e.g., it's CI
|
||||||
@@ -735,9 +703,6 @@ def _apply_section(dest, src):
|
|||||||
for value in header.values():
|
for value in header.values():
|
||||||
value = os.path.expandvars(value)
|
value = os.path.expandvars(value)
|
||||||
|
|
||||||
verify_ssl = mapping.get("verify_ssl", spack.config.get("config:verify_ssl", True))
|
|
||||||
timeout = mapping.get("timeout", spack.config.get("config:connect_timeout", 1))
|
|
||||||
|
|
||||||
required = mapping.get("require", [])
|
required = mapping.get("require", [])
|
||||||
allowed = mapping.get("allow", [])
|
allowed = mapping.get("allow", [])
|
||||||
ignored = mapping.get("ignore", [])
|
ignored = mapping.get("ignore", [])
|
||||||
@@ -771,19 +736,15 @@ def job_query(job):
|
|||||||
endpoint_url._replace(query=query).geturl(), headers=header, method="GET"
|
endpoint_url._replace(query=query).geturl(), headers=header, method="GET"
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
response = _dyn_mapping_urlopener(
|
response = _urlopen(request)
|
||||||
request, verify_ssl=verify_ssl, timeout=timeout
|
config = json.load(response)
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# For now just ignore any errors from dynamic mapping and continue
|
# For now just ignore any errors from dynamic mapping and continue
|
||||||
# This is still experimental, and failures should not stop CI
|
# This is still experimental, and failures should not stop CI
|
||||||
# from running normally
|
# from running normally
|
||||||
tty.warn(f"Failed to fetch dynamic mapping for query:\n\t{query}")
|
tty.warn(f"Failed to fetch dynamic mapping for query:\n\t{query}: {e}")
|
||||||
tty.warn(f"{e}")
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
config = json.load(codecs.getreader("utf-8")(response))
|
|
||||||
|
|
||||||
# Strip ignore keys
|
# Strip ignore keys
|
||||||
if ignored:
|
if ignored:
|
||||||
for key in ignored:
|
for key in ignored:
|
||||||
|
@@ -202,7 +202,7 @@ def _concretize_spec_pairs(
|
|||||||
# Special case for concretizing a single spec
|
# Special case for concretizing a single spec
|
||||||
if len(to_concretize) == 1:
|
if len(to_concretize) == 1:
|
||||||
abstract, concrete = to_concretize[0]
|
abstract, concrete = to_concretize[0]
|
||||||
return [concrete or abstract.concretized(tests=tests)]
|
return [concrete or spack.concretize.concretize_one(abstract, tests=tests)]
|
||||||
|
|
||||||
# Special case if every spec is either concrete or has an abstract hash
|
# Special case if every spec is either concrete or has an abstract hash
|
||||||
if all(
|
if all(
|
||||||
@@ -254,9 +254,9 @@ def matching_spec_from_env(spec):
|
|||||||
"""
|
"""
|
||||||
env = ev.active_environment()
|
env = ev.active_environment()
|
||||||
if env:
|
if env:
|
||||||
return env.matching_spec(spec) or spec.concretized()
|
return env.matching_spec(spec) or spack.concretize.concretize_one(spec)
|
||||||
else:
|
else:
|
||||||
return spec.concretized()
|
return spack.concretize.concretize_one(spec)
|
||||||
|
|
||||||
|
|
||||||
def matching_specs_from_env(specs):
|
def matching_specs_from_env(specs):
|
||||||
@@ -297,7 +297,7 @@ def disambiguate_spec(
|
|||||||
|
|
||||||
def disambiguate_spec_from_hashes(
|
def disambiguate_spec_from_hashes(
|
||||||
spec: spack.spec.Spec,
|
spec: spack.spec.Spec,
|
||||||
hashes: List[str],
|
hashes: Optional[List[str]],
|
||||||
local: bool = False,
|
local: bool = False,
|
||||||
installed: Union[bool, InstallRecordStatus] = True,
|
installed: Union[bool, InstallRecordStatus] = True,
|
||||||
first: bool = False,
|
first: bool = False,
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import warnings
|
||||||
|
|
||||||
import archspec.cpu
|
import archspec.cpu
|
||||||
|
|
||||||
@@ -51,10 +52,10 @@ def setup_parser(subparser):
|
|||||||
"-t", "--target", action="store_true", default=False, help="print only the target"
|
"-t", "--target", action="store_true", default=False, help="print only the target"
|
||||||
)
|
)
|
||||||
parts2.add_argument(
|
parts2.add_argument(
|
||||||
"-f", "--frontend", action="store_true", default=False, help="print frontend"
|
"-f", "--frontend", action="store_true", default=False, help="print frontend (DEPRECATED)"
|
||||||
)
|
)
|
||||||
parts2.add_argument(
|
parts2.add_argument(
|
||||||
"-b", "--backend", action="store_true", default=False, help="print backend"
|
"-b", "--backend", action="store_true", default=False, help="print backend (DEPRECATED)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -98,15 +99,14 @@ def arch(parser, args):
|
|||||||
display_targets(archspec.cpu.TARGETS)
|
display_targets(archspec.cpu.TARGETS)
|
||||||
return
|
return
|
||||||
|
|
||||||
os_args, target_args = "default_os", "default_target"
|
|
||||||
if args.frontend:
|
if args.frontend:
|
||||||
os_args, target_args = "frontend", "frontend"
|
warnings.warn("the argument --frontend is deprecated, and will be removed in Spack v1.0")
|
||||||
elif args.backend:
|
elif args.backend:
|
||||||
os_args, target_args = "backend", "backend"
|
warnings.warn("the argument --backend is deprecated, and will be removed in Spack v1.0")
|
||||||
|
|
||||||
host_platform = spack.platforms.host()
|
host_platform = spack.platforms.host()
|
||||||
host_os = host_platform.operating_system(os_args)
|
host_os = host_platform.default_operating_system()
|
||||||
host_target = host_platform.target(target_args)
|
host_target = host_platform.default_target()
|
||||||
if args.family:
|
if args.family:
|
||||||
host_target = host_target.family
|
host_target = host_target.family
|
||||||
elif args.generic:
|
elif args.generic:
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
# Copyright Spack Project Developers. See COPYRIGHT file for details.
|
# Copyright Spack Project Developers. See COPYRIGHT file for details.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
import os.path
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -14,9 +14,9 @@
|
|||||||
import spack.bootstrap
|
import spack.bootstrap
|
||||||
import spack.bootstrap.config
|
import spack.bootstrap.config
|
||||||
import spack.bootstrap.core
|
import spack.bootstrap.core
|
||||||
|
import spack.concretize
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.mirrors.utils
|
import spack.mirrors.utils
|
||||||
import spack.spec
|
|
||||||
import spack.stage
|
import spack.stage
|
||||||
import spack.util.path
|
import spack.util.path
|
||||||
import spack.util.spack_yaml
|
import spack.util.spack_yaml
|
||||||
@@ -397,7 +397,7 @@ def _mirror(args):
|
|||||||
llnl.util.tty.msg(msg.format(spec_str, mirror_dir))
|
llnl.util.tty.msg(msg.format(spec_str, mirror_dir))
|
||||||
# Suppress tty from the call below for terser messages
|
# Suppress tty from the call below for terser messages
|
||||||
llnl.util.tty.set_msg_enabled(False)
|
llnl.util.tty.set_msg_enabled(False)
|
||||||
spec = spack.spec.Spec(spec_str).concretized()
|
spec = spack.concretize.concretize_one(spec_str)
|
||||||
for node in spec.traverse():
|
for node in spec.traverse():
|
||||||
spack.mirrors.utils.create(mirror_dir, [node])
|
spack.mirrors.utils.create(mirror_dir, [node])
|
||||||
llnl.util.tty.set_msg_enabled(True)
|
llnl.util.tty.set_msg_enabled(True)
|
||||||
@@ -436,6 +436,7 @@ def write_metadata(subdir, metadata):
|
|||||||
shutil.copy(spack.util.path.canonicalize_path(GNUPG_JSON), abs_directory)
|
shutil.copy(spack.util.path.canonicalize_path(GNUPG_JSON), abs_directory)
|
||||||
shutil.copy(spack.util.path.canonicalize_path(PATCHELF_JSON), abs_directory)
|
shutil.copy(spack.util.path.canonicalize_path(PATCHELF_JSON), abs_directory)
|
||||||
instructions += cmd.format("local-binaries", rel_directory)
|
instructions += cmd.format("local-binaries", rel_directory)
|
||||||
|
instructions += " % spack buildcache update-index <final-path>/bootstrap_cache\n"
|
||||||
print(instructions)
|
print(instructions)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
import spack.binary_distribution as bindist
|
import spack.binary_distribution as bindist
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
import spack.concretize
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.deptypes as dt
|
import spack.deptypes as dt
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
@@ -554,8 +555,7 @@ def check_fn(args: argparse.Namespace):
|
|||||||
tty.msg("No specs provided, exiting.")
|
tty.msg("No specs provided, exiting.")
|
||||||
return
|
return
|
||||||
|
|
||||||
for spec in specs:
|
specs = [spack.concretize.concretize_one(s) for s in specs]
|
||||||
spec.concretize()
|
|
||||||
|
|
||||||
# Next see if there are any configured binary mirrors
|
# Next see if there are any configured binary mirrors
|
||||||
configured_mirrors = spack.config.get("mirrors", scope=args.scope)
|
configured_mirrors = spack.config.get("mirrors", scope=args.scope)
|
||||||
@@ -623,7 +623,7 @@ def save_specfile_fn(args):
|
|||||||
root = specs[0]
|
root = specs[0]
|
||||||
|
|
||||||
if not root.concrete:
|
if not root.concrete:
|
||||||
root.concretize()
|
root = spack.concretize.concretize_one(root)
|
||||||
|
|
||||||
save_dependency_specfiles(
|
save_dependency_specfiles(
|
||||||
root, args.specfile_dir, dependencies=spack.cmd.parse_specs(args.specs)
|
root, args.specfile_dir, dependencies=spack.cmd.parse_specs(args.specs)
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os.path
|
import os
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
from llnl.util.lang import stable_partition
|
from llnl.util.lang import stable_partition
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
|
|
||||||
import llnl.util.tty
|
import llnl.util.tty
|
||||||
|
|
||||||
|
@@ -86,8 +86,8 @@ def create_db_tarball(args):
|
|||||||
|
|
||||||
def report(args):
|
def report(args):
|
||||||
host_platform = spack.platforms.host()
|
host_platform = spack.platforms.host()
|
||||||
host_os = host_platform.operating_system("frontend")
|
host_os = host_platform.default_operating_system()
|
||||||
host_target = host_platform.target("frontend")
|
host_target = host_platform.default_target()
|
||||||
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:**", spack.get_version())
|
||||||
print("* **Python:**", platform.python_version())
|
print("* **Python:**", platform.python_version())
|
||||||
|
@@ -9,9 +9,9 @@
|
|||||||
|
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.package_base
|
|
||||||
import spack.store
|
import spack.store
|
||||||
from spack.cmd.common import arguments
|
from spack.cmd.common import arguments
|
||||||
|
from spack.solver.input_analysis import create_graph_analyzer
|
||||||
|
|
||||||
description = "show dependencies of a package"
|
description = "show dependencies of a package"
|
||||||
section = "basic"
|
section = "basic"
|
||||||
@@ -68,15 +68,17 @@ def dependencies(parser, args):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
spec = specs[0]
|
spec = specs[0]
|
||||||
dependencies = spack.package_base.possible_dependencies(
|
dependencies, virtuals, _ = create_graph_analyzer().possible_dependencies(
|
||||||
spec,
|
spec,
|
||||||
transitive=args.transitive,
|
transitive=args.transitive,
|
||||||
expand_virtuals=args.expand_virtuals,
|
expand_virtuals=args.expand_virtuals,
|
||||||
depflag=args.deptype,
|
allowed_deps=args.deptype,
|
||||||
)
|
)
|
||||||
|
if not args.expand_virtuals:
|
||||||
|
dependencies.update(virtuals)
|
||||||
|
|
||||||
if spec.name in dependencies:
|
if spec.name in dependencies:
|
||||||
del dependencies[spec.name]
|
dependencies.remove(spec.name)
|
||||||
|
|
||||||
if dependencies:
|
if dependencies:
|
||||||
colify(sorted(dependencies))
|
colify(sorted(dependencies))
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
from llnl.util.symlink import symlink
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
import spack.concretize
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.installer
|
import spack.installer
|
||||||
import spack.store
|
import spack.store
|
||||||
@@ -103,7 +104,7 @@ def deprecate(parser, args):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if args.install:
|
if args.install:
|
||||||
deprecator = specs[1].concretized()
|
deprecator = spack.concretize.concretize_one(specs[1])
|
||||||
else:
|
else:
|
||||||
deprecator = spack.cmd.disambiguate_spec(specs[1], env, local=True)
|
deprecator = spack.cmd.disambiguate_spec(specs[1], env, local=True)
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
import spack.build_environment
|
import spack.build_environment
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
import spack.cmd.common.arguments
|
import spack.cmd.common.arguments
|
||||||
|
import spack.concretize
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.repo
|
import spack.repo
|
||||||
from spack.cmd.common import arguments
|
from spack.cmd.common import arguments
|
||||||
@@ -113,8 +114,8 @@ def dev_build(self, args):
|
|||||||
source_path = os.path.abspath(source_path)
|
source_path = os.path.abspath(source_path)
|
||||||
|
|
||||||
# Forces the build to run out of the source directory.
|
# Forces the build to run out of the source directory.
|
||||||
spec.constrain("dev_path=%s" % source_path)
|
spec.constrain(f'dev_path="{source_path}"')
|
||||||
spec.concretize()
|
spec = spack.concretize.concretize_one(spec)
|
||||||
|
|
||||||
if spec.installed:
|
if spec.installed:
|
||||||
tty.error("Already installed in %s" % spec.prefix)
|
tty.error("Already installed in %s" % spec.prefix)
|
||||||
|
@@ -125,7 +125,7 @@ def develop(parser, args):
|
|||||||
version = spec.versions.concrete_range_as_version
|
version = spec.versions.concrete_range_as_version
|
||||||
if not version:
|
if not version:
|
||||||
# look up the maximum version so infintiy versions are preferred for develop
|
# look up the maximum version so infintiy versions are preferred for develop
|
||||||
version = max(spec.package_class.versions.keys())
|
version = max(spack.repo.PATH.get_pkg_class(spec.fullname).versions.keys())
|
||||||
tty.msg(f"Defaulting to highest version: {spec.name}@{version}")
|
tty.msg(f"Defaulting to highest version: {spec.name}@{version}")
|
||||||
spec.versions = spack.version.VersionList([version])
|
spec.versions = spack.version.VersionList([version])
|
||||||
|
|
||||||
|
@@ -110,10 +110,7 @@ def external_find(args):
|
|||||||
# Note that KeyboardInterrupt does not subclass Exception
|
# Note that KeyboardInterrupt does not subclass Exception
|
||||||
# (so CTRL-C will terminate the program as expected).
|
# (so CTRL-C will terminate the program as expected).
|
||||||
skip_msg = "Skipping manifest and continuing with other external checks"
|
skip_msg = "Skipping manifest and continuing with other external checks"
|
||||||
if (isinstance(e, IOError) or isinstance(e, OSError)) and e.errno in [
|
if isinstance(e, OSError) and e.errno in (errno.EPERM, errno.EACCES):
|
||||||
errno.EPERM,
|
|
||||||
errno.EACCES,
|
|
||||||
]:
|
|
||||||
# The manifest file does not have sufficient permissions enabled:
|
# The manifest file does not have sufficient permissions enabled:
|
||||||
# print a warning and keep going
|
# print a warning and keep going
|
||||||
tty.warn("Unable to read manifest due to insufficient permissions.", skip_msg)
|
tty.warn("Unable to read manifest due to insufficient permissions.", skip_msg)
|
||||||
|
@@ -54,10 +54,6 @@
|
|||||||
@m{target=target} specific <target> processor
|
@m{target=target} specific <target> processor
|
||||||
@m{arch=platform-os-target} shortcut for all three above
|
@m{arch=platform-os-target} shortcut for all three above
|
||||||
|
|
||||||
cross-compiling:
|
|
||||||
@m{os=backend} or @m{os=be} build for compute node (backend)
|
|
||||||
@m{os=frontend} or @m{os=fe} build for login node (frontend)
|
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
^dependency [constraints] specify constraints on dependencies
|
^dependency [constraints] specify constraints on dependencies
|
||||||
^@K{/hash} build with a specific installed
|
^@K{/hash} build with a specific installed
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
from llnl.util import lang, tty
|
from llnl.util import lang, tty
|
||||||
|
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
import spack.concretize
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.paths
|
import spack.paths
|
||||||
@@ -450,7 +451,7 @@ def concrete_specs_from_file(args):
|
|||||||
else:
|
else:
|
||||||
s = spack.spec.Spec.from_json(f)
|
s = spack.spec.Spec.from_json(f)
|
||||||
|
|
||||||
concretized = s.concretized()
|
concretized = spack.concretize.concretize_one(s)
|
||||||
if concretized.dag_hash() != s.dag_hash():
|
if concretized.dag_hash() != s.dag_hash():
|
||||||
msg = 'skipped invalid file "{0}". '
|
msg = 'skipped invalid file "{0}". '
|
||||||
msg += "The file does not contain a concrete spec."
|
msg += "The file does not contain a concrete spec."
|
||||||
|
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
from llnl.path import convert_to_posix_path
|
from llnl.path import convert_to_posix_path
|
||||||
|
|
||||||
|
import spack.concretize
|
||||||
import spack.paths
|
import spack.paths
|
||||||
import spack.util.executable
|
import spack.util.executable
|
||||||
from spack.spec import Spec
|
|
||||||
|
|
||||||
description = "generate Windows installer"
|
description = "generate Windows installer"
|
||||||
section = "admin"
|
section = "admin"
|
||||||
@@ -65,8 +65,7 @@ def make_installer(parser, args):
|
|||||||
"""
|
"""
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
output_dir = args.output_dir
|
output_dir = args.output_dir
|
||||||
cmake_spec = Spec("cmake")
|
cmake_spec = spack.concretize.concretize_one("cmake")
|
||||||
cmake_spec.concretize()
|
|
||||||
cmake_path = os.path.join(cmake_spec.prefix, "bin", "cmake.exe")
|
cmake_path = os.path.join(cmake_spec.prefix, "bin", "cmake.exe")
|
||||||
cpack_path = os.path.join(cmake_spec.prefix, "bin", "cpack.exe")
|
cpack_path = os.path.join(cmake_spec.prefix, "bin", "cpack.exe")
|
||||||
spack_source = args.spack_source
|
spack_source = args.spack_source
|
||||||
|
@@ -492,7 +492,7 @@ def extend_with_additional_versions(specs, num_versions):
|
|||||||
mirror_specs = spack.mirrors.utils.get_all_versions(specs)
|
mirror_specs = spack.mirrors.utils.get_all_versions(specs)
|
||||||
else:
|
else:
|
||||||
mirror_specs = spack.mirrors.utils.get_matching_versions(specs, num_versions=num_versions)
|
mirror_specs = spack.mirrors.utils.get_matching_versions(specs, num_versions=num_versions)
|
||||||
mirror_specs = [x.concretized() for x in mirror_specs]
|
mirror_specs = [spack.concretize.concretize_one(x) for x in mirror_specs]
|
||||||
return mirror_specs
|
return mirror_specs
|
||||||
|
|
||||||
|
|
||||||
@@ -545,7 +545,7 @@ def _not_license_excluded(self, x):
|
|||||||
package does not explicitly forbid redistributing source."""
|
package does not explicitly forbid redistributing source."""
|
||||||
if self.private:
|
if self.private:
|
||||||
return True
|
return True
|
||||||
elif x.package_class.redistribute_source(x):
|
elif spack.repo.PATH.get_pkg_class(x.fullname).redistribute_source(x):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
tty.debug(
|
tty.debug(
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
"""Implementation details of the ``spack module`` command."""
|
"""Implementation details of the ``spack module`` command."""
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import os.path
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@@ -41,7 +41,11 @@ def providers(parser, args):
|
|||||||
specs = spack.cmd.parse_specs(args.virtual_package)
|
specs = spack.cmd.parse_specs(args.virtual_package)
|
||||||
|
|
||||||
# Check prerequisites
|
# Check prerequisites
|
||||||
non_virtual = [str(s) for s in specs if not s.virtual or s.name not in valid_virtuals]
|
non_virtual = [
|
||||||
|
str(s)
|
||||||
|
for s in specs
|
||||||
|
if not spack.repo.PATH.is_virtual(s.name) or s.name not in valid_virtuals
|
||||||
|
]
|
||||||
if non_virtual:
|
if non_virtual:
|
||||||
msg = "non-virtual specs cannot be part of the query "
|
msg = "non-virtual specs cannot be part of the query "
|
||||||
msg += "[{0}]\n".format(", ".join(non_virtual))
|
msg += "[{0}]\n".format(", ".join(non_virtual))
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from itertools import zip_longest
|
from itertools import islice, zip_longest
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
@@ -423,7 +423,8 @@ def _run_import_check(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
for m in is_abs_import.finditer(contents):
|
for m in is_abs_import.finditer(contents):
|
||||||
if contents.count(m.group(1)) == 1:
|
# Find at most two occurences: the first is the import itself, the second is its usage.
|
||||||
|
if len(list(islice(re.finditer(rf"{re.escape(m.group(1))}(?!\w)", contents), 2))) == 1:
|
||||||
to_remove.append(m.group(0))
|
to_remove.append(m.group(0))
|
||||||
exit_code = 1
|
exit_code = 1
|
||||||
print(f"{pretty_path}: redundant import: {m.group(1)}", file=out)
|
print(f"{pretty_path}: redundant import: {m.group(1)}", file=out)
|
||||||
@@ -438,7 +439,7 @@ def _run_import_check(
|
|||||||
module = _module_part(root, m.group(0))
|
module = _module_part(root, m.group(0))
|
||||||
if not module or module in to_add:
|
if not module or module in to_add:
|
||||||
continue
|
continue
|
||||||
if re.search(rf"import {re.escape(module)}\b(?!\.)", contents):
|
if re.search(rf"import {re.escape(module)}(?!\w|\.)", contents):
|
||||||
continue
|
continue
|
||||||
to_add.add(module)
|
to_add.add(module)
|
||||||
exit_code = 1
|
exit_code = 1
|
||||||
|
@@ -177,16 +177,15 @@ def test_run(args):
|
|||||||
matching = spack.store.STORE.db.query_local(spec, hashes=hashes, explicit=explicit)
|
matching = spack.store.STORE.db.query_local(spec, hashes=hashes, explicit=explicit)
|
||||||
if spec and not matching:
|
if spec and not matching:
|
||||||
tty.warn("No {0}installed packages match spec {1}".format(explicit_str, spec))
|
tty.warn("No {0}installed packages match spec {1}".format(explicit_str, spec))
|
||||||
"""
|
|
||||||
TODO: Need to write out a log message and/or CDASH Testing
|
|
||||||
output that package not installed IF continue to process
|
|
||||||
these issues here.
|
|
||||||
|
|
||||||
if args.log_format:
|
# TODO: Need to write out a log message and/or CDASH Testing
|
||||||
# Proceed with the spec assuming the test process
|
# output that package not installed IF continue to process
|
||||||
# to ensure report package as skipped (e.g., for CI)
|
# these issues here.
|
||||||
specs_to_test.append(spec)
|
|
||||||
"""
|
# if args.log_format:
|
||||||
|
# # Proceed with the spec assuming the test process
|
||||||
|
# # to ensure report package as skipped (e.g., for CI)
|
||||||
|
# specs_to_test.append(spec)
|
||||||
|
|
||||||
specs_to_test.extend(matching)
|
specs_to_test.extend(matching)
|
||||||
|
|
||||||
@@ -253,7 +252,9 @@ def has_test_and_tags(pkg_class):
|
|||||||
hashes = env.all_hashes() if env else None
|
hashes = env.all_hashes() if env else None
|
||||||
|
|
||||||
specs = spack.store.STORE.db.query(hashes=hashes)
|
specs = spack.store.STORE.db.query(hashes=hashes)
|
||||||
specs = list(filter(lambda s: has_test_and_tags(s.package_class), specs))
|
specs = list(
|
||||||
|
filter(lambda s: has_test_and_tags(spack.repo.PATH.get_pkg_class(s.fullname)), specs)
|
||||||
|
)
|
||||||
|
|
||||||
spack.cmd.display_specs(specs, long=True)
|
spack.cmd.display_specs(specs, long=True)
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
import os.path
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import collections
|
import collections
|
||||||
import io
|
import io
|
||||||
import os.path
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@@ -801,17 +801,17 @@ def _extract_compiler_paths(spec: "spack.spec.Spec") -> Optional[Dict[str, str]]
|
|||||||
def _extract_os_and_target(spec: "spack.spec.Spec"):
|
def _extract_os_and_target(spec: "spack.spec.Spec"):
|
||||||
if not spec.architecture:
|
if not spec.architecture:
|
||||||
host_platform = spack.platforms.host()
|
host_platform = spack.platforms.host()
|
||||||
operating_system = host_platform.operating_system("default_os")
|
operating_system = host_platform.default_operating_system()
|
||||||
target = host_platform.target("default_target")
|
target = host_platform.default_target()
|
||||||
else:
|
else:
|
||||||
target = spec.architecture.target
|
target = spec.architecture.target
|
||||||
if not target:
|
if not target:
|
||||||
target = spack.platforms.host().target("default_target")
|
target = spack.platforms.host().default_target()
|
||||||
|
|
||||||
operating_system = spec.os
|
operating_system = spec.os
|
||||||
if not operating_system:
|
if not operating_system:
|
||||||
host_platform = spack.platforms.host()
|
host_platform = spack.platforms.host()
|
||||||
operating_system = host_platform.operating_system("default_os")
|
operating_system = host_platform.default_operating_system()
|
||||||
return operating_system, target
|
return operating_system, target
|
||||||
|
|
||||||
|
|
||||||
|
@@ -37,13 +37,12 @@ def enable_compiler_existence_check():
|
|||||||
|
|
||||||
SpecPairInput = Tuple[Spec, Optional[Spec]]
|
SpecPairInput = Tuple[Spec, Optional[Spec]]
|
||||||
SpecPair = Tuple[Spec, Spec]
|
SpecPair = Tuple[Spec, Spec]
|
||||||
SpecLike = Union[Spec, str]
|
|
||||||
TestsType = Union[bool, Iterable[str]]
|
TestsType = Union[bool, Iterable[str]]
|
||||||
|
|
||||||
|
|
||||||
def concretize_specs_together(
|
def _concretize_specs_together(
|
||||||
abstract_specs: Sequence[SpecLike], tests: TestsType = False
|
abstract_specs: Sequence[Spec], tests: TestsType = False
|
||||||
) -> Sequence[Spec]:
|
) -> List[Spec]:
|
||||||
"""Given a number of specs as input, tries to concretize them together.
|
"""Given a number of specs as input, tries to concretize them together.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -51,11 +50,10 @@ def concretize_specs_together(
|
|||||||
tests: list of package names for which to consider tests dependencies. If True, all nodes
|
tests: list of package names for which to consider tests dependencies. If True, all nodes
|
||||||
will have test dependencies. If False, test dependencies will be disregarded.
|
will have test dependencies. If False, test dependencies will be disregarded.
|
||||||
"""
|
"""
|
||||||
import spack.solver.asp
|
from spack.solver.asp import Solver
|
||||||
|
|
||||||
allow_deprecated = spack.config.get("config:deprecated", False)
|
allow_deprecated = spack.config.get("config:deprecated", False)
|
||||||
solver = spack.solver.asp.Solver()
|
result = Solver().solve(abstract_specs, tests=tests, allow_deprecated=allow_deprecated)
|
||||||
result = solver.solve(abstract_specs, tests=tests, allow_deprecated=allow_deprecated)
|
|
||||||
return [s.copy() for s in result.specs]
|
return [s.copy() for s in result.specs]
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +70,7 @@ def concretize_together(
|
|||||||
"""
|
"""
|
||||||
to_concretize = [concrete if concrete else abstract for abstract, concrete in spec_list]
|
to_concretize = [concrete if concrete else abstract for abstract, concrete in spec_list]
|
||||||
abstract_specs = [abstract for abstract, _ in spec_list]
|
abstract_specs = [abstract for abstract, _ in spec_list]
|
||||||
concrete_specs = concretize_specs_together(to_concretize, tests=tests)
|
concrete_specs = _concretize_specs_together(to_concretize, tests=tests)
|
||||||
return list(zip(abstract_specs, concrete_specs))
|
return list(zip(abstract_specs, concrete_specs))
|
||||||
|
|
||||||
|
|
||||||
@@ -90,7 +88,7 @@ def concretize_together_when_possible(
|
|||||||
tests: list of package names for which to consider tests dependencies. If True, all nodes
|
tests: list of package names for which to consider tests dependencies. If True, all nodes
|
||||||
will have test dependencies. If False, test dependencies will be disregarded.
|
will have test dependencies. If False, test dependencies will be disregarded.
|
||||||
"""
|
"""
|
||||||
import spack.solver.asp
|
from spack.solver.asp import Solver
|
||||||
|
|
||||||
to_concretize = [concrete if concrete else abstract for abstract, concrete in spec_list]
|
to_concretize = [concrete if concrete else abstract for abstract, concrete in spec_list]
|
||||||
old_concrete_to_abstract = {
|
old_concrete_to_abstract = {
|
||||||
@@ -98,9 +96,8 @@ def concretize_together_when_possible(
|
|||||||
}
|
}
|
||||||
|
|
||||||
result_by_user_spec = {}
|
result_by_user_spec = {}
|
||||||
solver = spack.solver.asp.Solver()
|
|
||||||
allow_deprecated = spack.config.get("config:deprecated", False)
|
allow_deprecated = spack.config.get("config:deprecated", False)
|
||||||
for result in solver.solve_in_rounds(
|
for result in Solver().solve_in_rounds(
|
||||||
to_concretize, tests=tests, allow_deprecated=allow_deprecated
|
to_concretize, tests=tests, allow_deprecated=allow_deprecated
|
||||||
):
|
):
|
||||||
result_by_user_spec.update(result.specs_by_input)
|
result_by_user_spec.update(result.specs_by_input)
|
||||||
@@ -124,7 +121,7 @@ def concretize_separately(
|
|||||||
tests: list of package names for which to consider tests dependencies. If True, all nodes
|
tests: list of package names for which to consider tests dependencies. If True, all nodes
|
||||||
will have test dependencies. If False, test dependencies will be disregarded.
|
will have test dependencies. If False, test dependencies will be disregarded.
|
||||||
"""
|
"""
|
||||||
import spack.bootstrap
|
from spack.bootstrap import ensure_bootstrap_configuration, ensure_clingo_importable_or_raise
|
||||||
|
|
||||||
to_concretize = [abstract for abstract, concrete in spec_list if not concrete]
|
to_concretize = [abstract for abstract, concrete in spec_list if not concrete]
|
||||||
args = [
|
args = [
|
||||||
@@ -134,8 +131,8 @@ def concretize_separately(
|
|||||||
]
|
]
|
||||||
ret = [(i, abstract) for i, abstract in enumerate(to_concretize) if abstract.concrete]
|
ret = [(i, abstract) for i, abstract in enumerate(to_concretize) if abstract.concrete]
|
||||||
# Ensure we don't try to bootstrap clingo in parallel
|
# Ensure we don't try to bootstrap clingo in parallel
|
||||||
with spack.bootstrap.ensure_bootstrap_configuration():
|
with ensure_bootstrap_configuration():
|
||||||
spack.bootstrap.ensure_clingo_importable_or_raise()
|
ensure_clingo_importable_or_raise()
|
||||||
|
|
||||||
# Ensure all the indexes have been built or updated, since
|
# Ensure all the indexes have been built or updated, since
|
||||||
# otherwise the processes in the pool may timeout on waiting
|
# otherwise the processes in the pool may timeout on waiting
|
||||||
@@ -190,10 +187,52 @@ def _concretize_task(packed_arguments: Tuple[int, str, TestsType]) -> Tuple[int,
|
|||||||
index, spec_str, tests = packed_arguments
|
index, spec_str, tests = packed_arguments
|
||||||
with tty.SuppressOutput(msg_enabled=False):
|
with tty.SuppressOutput(msg_enabled=False):
|
||||||
start = time.time()
|
start = time.time()
|
||||||
spec = Spec(spec_str).concretized(tests=tests)
|
spec = concretize_one(Spec(spec_str), tests=tests)
|
||||||
return index, spec, time.time() - start
|
return index, spec, time.time() - start
|
||||||
|
|
||||||
|
|
||||||
|
def concretize_one(spec: Union[str, Spec], tests: TestsType = False) -> Spec:
|
||||||
|
"""Return a concretized copy of the given spec.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tests: if False disregard 'test' dependencies, if a list of names activate them for
|
||||||
|
the packages in the list, if True activate 'test' dependencies for all packages.
|
||||||
|
"""
|
||||||
|
from spack.solver.asp import Solver, SpecBuilder
|
||||||
|
|
||||||
|
if isinstance(spec, str):
|
||||||
|
spec = Spec(spec)
|
||||||
|
spec = spec.lookup_hash()
|
||||||
|
|
||||||
|
if spec.concrete:
|
||||||
|
return spec.copy()
|
||||||
|
|
||||||
|
for node in spec.traverse():
|
||||||
|
if not node.name:
|
||||||
|
raise spack.error.SpecError(
|
||||||
|
f"Spec {node} has no name; cannot concretize an anonymous spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
allow_deprecated = spack.config.get("config:deprecated", False)
|
||||||
|
result = Solver().solve([spec], tests=tests, allow_deprecated=allow_deprecated)
|
||||||
|
|
||||||
|
# take the best answer
|
||||||
|
opt, i, answer = min(result.answers)
|
||||||
|
name = spec.name
|
||||||
|
# TODO: Consolidate this code with similar code in solve.py
|
||||||
|
if spack.repo.PATH.is_virtual(spec.name):
|
||||||
|
providers = [s.name for s in answer.values() if s.package.provides(name)]
|
||||||
|
name = providers[0]
|
||||||
|
|
||||||
|
node = SpecBuilder.make_node(pkg=name)
|
||||||
|
assert (
|
||||||
|
node in answer
|
||||||
|
), f"cannot find {name} in the list of specs {','.join([n.pkg for n in answer.keys()])}"
|
||||||
|
|
||||||
|
concretized = answer[node]
|
||||||
|
return concretized
|
||||||
|
|
||||||
|
|
||||||
class UnavailableCompilerVersionError(spack.error.SpackError):
|
class UnavailableCompilerVersionError(spack.error.SpackError):
|
||||||
"""Raised when there is no available compiler that satisfies a
|
"""Raised when there is no available compiler that satisfies a
|
||||||
compiler spec."""
|
compiler spec."""
|
||||||
|
@@ -36,6 +36,8 @@
|
|||||||
import sys
|
import sys
|
||||||
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Union
|
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
import jsonschema
|
||||||
|
|
||||||
from llnl.util import filesystem, lang, tty
|
from llnl.util import filesystem, lang, tty
|
||||||
|
|
||||||
import spack.error
|
import spack.error
|
||||||
@@ -51,6 +53,7 @@
|
|||||||
import spack.schema.definitions
|
import spack.schema.definitions
|
||||||
import spack.schema.develop
|
import spack.schema.develop
|
||||||
import spack.schema.env
|
import spack.schema.env
|
||||||
|
import spack.schema.env_vars
|
||||||
import spack.schema.mirrors
|
import spack.schema.mirrors
|
||||||
import spack.schema.modules
|
import spack.schema.modules
|
||||||
import spack.schema.packages
|
import spack.schema.packages
|
||||||
@@ -68,6 +71,7 @@
|
|||||||
"compilers": spack.schema.compilers.schema,
|
"compilers": spack.schema.compilers.schema,
|
||||||
"concretizer": spack.schema.concretizer.schema,
|
"concretizer": spack.schema.concretizer.schema,
|
||||||
"definitions": spack.schema.definitions.schema,
|
"definitions": spack.schema.definitions.schema,
|
||||||
|
"env_vars": spack.schema.env_vars.schema,
|
||||||
"view": spack.schema.view.schema,
|
"view": spack.schema.view.schema,
|
||||||
"develop": spack.schema.develop.schema,
|
"develop": spack.schema.develop.schema,
|
||||||
"mirrors": spack.schema.mirrors.schema,
|
"mirrors": spack.schema.mirrors.schema,
|
||||||
@@ -1048,8 +1052,6 @@ def validate(
|
|||||||
This leverages the line information (start_mark, end_mark) stored
|
This leverages the line information (start_mark, end_mark) stored
|
||||||
on Spack YAML structures.
|
on Spack YAML structures.
|
||||||
"""
|
"""
|
||||||
import jsonschema
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
spack.schema.Validator(schema).validate(data)
|
spack.schema.Validator(schema).validate(data)
|
||||||
except jsonschema.ValidationError as e:
|
except jsonschema.ValidationError as e:
|
||||||
|
@@ -6,6 +6,8 @@
|
|||||||
"""
|
"""
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
import jsonschema
|
||||||
|
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.schema.env as env
|
import spack.schema.env as env
|
||||||
import spack.util.spack_yaml as syaml
|
import spack.util.spack_yaml as syaml
|
||||||
@@ -30,8 +32,6 @@ def validate(configuration_file):
|
|||||||
Returns:
|
Returns:
|
||||||
A sanitized copy of the configuration stored in the input file
|
A sanitized copy of the configuration stored in the input file
|
||||||
"""
|
"""
|
||||||
import jsonschema
|
|
||||||
|
|
||||||
with open(configuration_file, encoding="utf-8") as f:
|
with open(configuration_file, encoding="utf-8") as f:
|
||||||
config = syaml.load(f)
|
config = syaml.load(f)
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ def validate(configuration_file):
|
|||||||
# Set the default value of the concretization strategy to unify and
|
# Set the default value of the concretization strategy to unify and
|
||||||
# warn if the user explicitly set another value
|
# warn if the user explicitly set another value
|
||||||
env_dict.setdefault("concretizer", {"unify": True})
|
env_dict.setdefault("concretizer", {"unify": True})
|
||||||
if not env_dict["concretizer"]["unify"] is True:
|
if env_dict["concretizer"]["unify"] is not True:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'"concretizer:unify" is not set to "true", which means the '
|
'"concretizer:unify" is not set to "true", which means the '
|
||||||
"generated image may contain different variants of the same "
|
"generated image may contain different variants of the same "
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
"""Manages the details on the images used in the various stages."""
|
"""Manages the details on the images used in the various stages."""
|
||||||
import json
|
import json
|
||||||
import os.path
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import jsonschema
|
||||||
|
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.error
|
import spack.error
|
||||||
import spack.schema.env
|
import spack.schema.env
|
||||||
@@ -188,8 +190,6 @@ def paths(self):
|
|||||||
@tengine.context_property
|
@tengine.context_property
|
||||||
def manifest(self):
|
def manifest(self):
|
||||||
"""The spack.yaml file that should be used in the image"""
|
"""The spack.yaml file that should be used in the image"""
|
||||||
import jsonschema
|
|
||||||
|
|
||||||
# Copy in the part of spack.yaml prescribed in the configuration file
|
# Copy in the part of spack.yaml prescribed in the configuration file
|
||||||
manifest = copy.deepcopy(self.config)
|
manifest = copy.deepcopy(self.config)
|
||||||
manifest.pop("container")
|
manifest.pop("container")
|
||||||
|
@@ -41,6 +41,8 @@
|
|||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import spack.repo
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@@ -123,6 +125,15 @@
|
|||||||
"deprecated_for",
|
"deprecated_for",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#: File where the database is written
|
||||||
|
INDEX_JSON_FILE = "index.json"
|
||||||
|
|
||||||
|
# Verifier file to check last modification of the DB
|
||||||
|
_INDEX_VERIFIER_FILE = "index_verifier"
|
||||||
|
|
||||||
|
# Lockfile for the database
|
||||||
|
_LOCK_FILE = "lock"
|
||||||
|
|
||||||
|
|
||||||
@llnl.util.lang.memoized
|
@llnl.util.lang.memoized
|
||||||
def _getfqdn():
|
def _getfqdn():
|
||||||
@@ -260,7 +271,7 @@ class ForbiddenLockError(SpackError):
|
|||||||
|
|
||||||
class ForbiddenLock:
|
class ForbiddenLock:
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
raise ForbiddenLockError("Cannot access attribute '{0}' of lock".format(name))
|
raise ForbiddenLockError(f"Cannot access attribute '{name}' of lock")
|
||||||
|
|
||||||
def __reduce__(self):
|
def __reduce__(self):
|
||||||
return ForbiddenLock, tuple()
|
return ForbiddenLock, tuple()
|
||||||
@@ -419,14 +430,25 @@ class FailureTracker:
|
|||||||
the likelihood of collision very low with no cleanup required.
|
the likelihood of collision very low with no cleanup required.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
#: root directory of the failure tracker
|
||||||
|
dir: pathlib.Path
|
||||||
|
|
||||||
|
#: File for locking particular concrete spec hashes
|
||||||
|
locker: SpecLocker
|
||||||
|
|
||||||
def __init__(self, root_dir: Union[str, pathlib.Path], default_timeout: Optional[float]):
|
def __init__(self, root_dir: Union[str, pathlib.Path], default_timeout: Optional[float]):
|
||||||
#: Ensure a persistent location for dealing with parallel installation
|
#: Ensure a persistent location for dealing with parallel installation
|
||||||
#: failures (e.g., across near-concurrent processes).
|
#: failures (e.g., across near-concurrent processes).
|
||||||
self.dir = pathlib.Path(root_dir) / _DB_DIRNAME / "failures"
|
self.dir = pathlib.Path(root_dir) / _DB_DIRNAME / "failures"
|
||||||
self.dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
self.locker = SpecLocker(failures_lock_path(root_dir), default_timeout=default_timeout)
|
self.locker = SpecLocker(failures_lock_path(root_dir), default_timeout=default_timeout)
|
||||||
|
|
||||||
|
def _ensure_parent_directories(self) -> None:
|
||||||
|
"""Ensure that parent directories of the FailureTracker exist.
|
||||||
|
|
||||||
|
Accesses the filesystem only once, the first time it's called on a given FailureTracker.
|
||||||
|
"""
|
||||||
|
self.dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
def clear(self, spec: "spack.spec.Spec", force: bool = False) -> None:
|
def clear(self, spec: "spack.spec.Spec", force: bool = False) -> None:
|
||||||
"""Removes any persistent and cached failure tracking for the spec.
|
"""Removes any persistent and cached failure tracking for the spec.
|
||||||
|
|
||||||
@@ -469,13 +491,18 @@ def clear_all(self) -> None:
|
|||||||
|
|
||||||
tty.debug("Removing prefix failure tracking files")
|
tty.debug("Removing prefix failure tracking files")
|
||||||
try:
|
try:
|
||||||
for fail_mark in os.listdir(str(self.dir)):
|
marks = os.listdir(str(self.dir))
|
||||||
try:
|
except FileNotFoundError:
|
||||||
(self.dir / fail_mark).unlink()
|
return # directory doesn't exist yet
|
||||||
except OSError as exc:
|
|
||||||
tty.warn(f"Unable to remove failure marking file {fail_mark}: {str(exc)}")
|
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
tty.warn(f"Unable to remove failure marking files: {str(exc)}")
|
tty.warn(f"Unable to remove failure marking files: {str(exc)}")
|
||||||
|
return
|
||||||
|
|
||||||
|
for fail_mark in marks:
|
||||||
|
try:
|
||||||
|
(self.dir / fail_mark).unlink()
|
||||||
|
except OSError as exc:
|
||||||
|
tty.warn(f"Unable to remove failure marking file {fail_mark}: {str(exc)}")
|
||||||
|
|
||||||
def mark(self, spec: "spack.spec.Spec") -> lk.Lock:
|
def mark(self, spec: "spack.spec.Spec") -> lk.Lock:
|
||||||
"""Marks a spec as failing to install.
|
"""Marks a spec as failing to install.
|
||||||
@@ -483,6 +510,8 @@ def mark(self, spec: "spack.spec.Spec") -> lk.Lock:
|
|||||||
Args:
|
Args:
|
||||||
spec: spec that failed to install
|
spec: spec that failed to install
|
||||||
"""
|
"""
|
||||||
|
self._ensure_parent_directories()
|
||||||
|
|
||||||
# Dump the spec to the failure file for (manual) debugging purposes
|
# Dump the spec to the failure file for (manual) debugging purposes
|
||||||
path = self._path(spec)
|
path = self._path(spec)
|
||||||
path.write_text(spec.to_json())
|
path.write_text(spec.to_json())
|
||||||
@@ -567,17 +596,13 @@ def __init__(
|
|||||||
Relevant only if the repository is not an upstream.
|
Relevant only if the repository is not an upstream.
|
||||||
"""
|
"""
|
||||||
self.root = root
|
self.root = root
|
||||||
self.database_directory = os.path.join(self.root, _DB_DIRNAME)
|
self.database_directory = pathlib.Path(self.root) / _DB_DIRNAME
|
||||||
self.layout = layout
|
self.layout = layout
|
||||||
|
|
||||||
# Set up layout of database files within the db dir
|
# Set up layout of database files within the db dir
|
||||||
self._index_path = os.path.join(self.database_directory, "index.json")
|
self._index_path = self.database_directory / INDEX_JSON_FILE
|
||||||
self._verifier_path = os.path.join(self.database_directory, "index_verifier")
|
self._verifier_path = self.database_directory / _INDEX_VERIFIER_FILE
|
||||||
self._lock_path = os.path.join(self.database_directory, "lock")
|
self._lock_path = self.database_directory / _LOCK_FILE
|
||||||
|
|
||||||
# Create needed directories and files
|
|
||||||
if not is_upstream and not os.path.exists(self.database_directory):
|
|
||||||
fs.mkdirp(self.database_directory)
|
|
||||||
|
|
||||||
self.is_upstream = is_upstream
|
self.is_upstream = is_upstream
|
||||||
self.last_seen_verifier = ""
|
self.last_seen_verifier = ""
|
||||||
@@ -592,14 +617,14 @@ def __init__(
|
|||||||
|
|
||||||
# initialize rest of state.
|
# initialize rest of state.
|
||||||
self.db_lock_timeout = lock_cfg.database_timeout
|
self.db_lock_timeout = lock_cfg.database_timeout
|
||||||
tty.debug("DATABASE LOCK TIMEOUT: {0}s".format(str(self.db_lock_timeout)))
|
tty.debug(f"DATABASE LOCK TIMEOUT: {str(self.db_lock_timeout)}s")
|
||||||
|
|
||||||
self.lock: Union[ForbiddenLock, lk.Lock]
|
self.lock: Union[ForbiddenLock, lk.Lock]
|
||||||
if self.is_upstream:
|
if self.is_upstream:
|
||||||
self.lock = ForbiddenLock()
|
self.lock = ForbiddenLock()
|
||||||
else:
|
else:
|
||||||
self.lock = lk.Lock(
|
self.lock = lk.Lock(
|
||||||
self._lock_path,
|
str(self._lock_path),
|
||||||
default_timeout=self.db_lock_timeout,
|
default_timeout=self.db_lock_timeout,
|
||||||
desc="database",
|
desc="database",
|
||||||
enable=lock_cfg.enable,
|
enable=lock_cfg.enable,
|
||||||
@@ -616,6 +641,11 @@ def __init__(
|
|||||||
self._write_transaction_impl = lk.WriteTransaction
|
self._write_transaction_impl = lk.WriteTransaction
|
||||||
self._read_transaction_impl = lk.ReadTransaction
|
self._read_transaction_impl = lk.ReadTransaction
|
||||||
|
|
||||||
|
def _ensure_parent_directories(self):
|
||||||
|
"""Create the parent directory for the DB, if necessary."""
|
||||||
|
if not self.is_upstream:
|
||||||
|
self.database_directory.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
def write_transaction(self):
|
def write_transaction(self):
|
||||||
"""Get a write lock context manager for use in a `with` block."""
|
"""Get a write lock context manager for use in a `with` block."""
|
||||||
return self._write_transaction_impl(self.lock, acquire=self._read, release=self._write)
|
return self._write_transaction_impl(self.lock, acquire=self._read, release=self._write)
|
||||||
@@ -630,6 +660,8 @@ def _write_to_file(self, stream):
|
|||||||
|
|
||||||
This function does not do any locking or transactions.
|
This function does not do any locking or transactions.
|
||||||
"""
|
"""
|
||||||
|
self._ensure_parent_directories()
|
||||||
|
|
||||||
# map from per-spec hash code to installation record.
|
# map from per-spec hash code to installation record.
|
||||||
installs = dict(
|
installs = dict(
|
||||||
(k, v.to_dict(include_fields=self.record_fields)) for k, v in self._data.items()
|
(k, v.to_dict(include_fields=self.record_fields)) for k, v in self._data.items()
|
||||||
@@ -759,7 +791,7 @@ def _read_from_file(self, filename):
|
|||||||
Does not do any locking.
|
Does not do any locking.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with open(filename, "r", encoding="utf-8") as f:
|
with open(str(filename), "r", encoding="utf-8") as f:
|
||||||
# In the future we may use a stream of JSON objects, hence `raw_decode` for compat.
|
# In the future we may use a stream of JSON objects, hence `raw_decode` for compat.
|
||||||
fdata, _ = JSONDecoder().raw_decode(f.read())
|
fdata, _ = JSONDecoder().raw_decode(f.read())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -860,11 +892,13 @@ def reindex(self):
|
|||||||
if self.is_upstream:
|
if self.is_upstream:
|
||||||
raise UpstreamDatabaseLockingError("Cannot reindex an upstream database")
|
raise UpstreamDatabaseLockingError("Cannot reindex an upstream database")
|
||||||
|
|
||||||
|
self._ensure_parent_directories()
|
||||||
|
|
||||||
# Special transaction to avoid recursive reindex calls and to
|
# Special transaction to avoid recursive reindex calls and to
|
||||||
# ignore errors if we need to rebuild a corrupt database.
|
# ignore errors if we need to rebuild a corrupt database.
|
||||||
def _read_suppress_error():
|
def _read_suppress_error():
|
||||||
try:
|
try:
|
||||||
if os.path.isfile(self._index_path):
|
if self._index_path.is_file():
|
||||||
self._read_from_file(self._index_path)
|
self._read_from_file(self._index_path)
|
||||||
except CorruptDatabaseError as e:
|
except CorruptDatabaseError as e:
|
||||||
tty.warn(f"Reindexing corrupt database, error was: {e}")
|
tty.warn(f"Reindexing corrupt database, error was: {e}")
|
||||||
@@ -1007,7 +1041,7 @@ def _check_ref_counts(self):
|
|||||||
% (key, found, expected, self._index_path)
|
% (key, found, expected, self._index_path)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _write(self, type, value, traceback):
|
def _write(self, type=None, value=None, traceback=None):
|
||||||
"""Write the in-memory database index to its file path.
|
"""Write the in-memory database index to its file path.
|
||||||
|
|
||||||
This is a helper function called by the WriteTransaction context
|
This is a helper function called by the WriteTransaction context
|
||||||
@@ -1018,6 +1052,8 @@ def _write(self, type, value, traceback):
|
|||||||
|
|
||||||
This routine does no locking.
|
This routine does no locking.
|
||||||
"""
|
"""
|
||||||
|
self._ensure_parent_directories()
|
||||||
|
|
||||||
# Do not write if exceptions were raised
|
# Do not write if exceptions were raised
|
||||||
if type is not None:
|
if type is not None:
|
||||||
# A failure interrupted a transaction, so we should record that
|
# A failure interrupted a transaction, so we should record that
|
||||||
@@ -1026,16 +1062,16 @@ def _write(self, type, value, traceback):
|
|||||||
self._state_is_inconsistent = True
|
self._state_is_inconsistent = True
|
||||||
return
|
return
|
||||||
|
|
||||||
temp_file = self._index_path + (".%s.%s.temp" % (_getfqdn(), os.getpid()))
|
temp_file = str(self._index_path) + (".%s.%s.temp" % (_getfqdn(), os.getpid()))
|
||||||
|
|
||||||
# Write a temporary database file them move it into place
|
# Write a temporary database file them move it into place
|
||||||
try:
|
try:
|
||||||
with open(temp_file, "w", encoding="utf-8") as f:
|
with open(temp_file, "w", encoding="utf-8") as f:
|
||||||
self._write_to_file(f)
|
self._write_to_file(f)
|
||||||
fs.rename(temp_file, self._index_path)
|
fs.rename(temp_file, str(self._index_path))
|
||||||
|
|
||||||
if _use_uuid:
|
if _use_uuid:
|
||||||
with open(self._verifier_path, "w", encoding="utf-8") as f:
|
with self._verifier_path.open("w", encoding="utf-8") as f:
|
||||||
new_verifier = str(uuid.uuid4())
|
new_verifier = str(uuid.uuid4())
|
||||||
f.write(new_verifier)
|
f.write(new_verifier)
|
||||||
self.last_seen_verifier = new_verifier
|
self.last_seen_verifier = new_verifier
|
||||||
@@ -1048,11 +1084,11 @@ def _write(self, type, value, traceback):
|
|||||||
|
|
||||||
def _read(self):
|
def _read(self):
|
||||||
"""Re-read Database from the data in the set location. This does no locking."""
|
"""Re-read Database from the data in the set location. This does no locking."""
|
||||||
if os.path.isfile(self._index_path):
|
if self._index_path.is_file():
|
||||||
current_verifier = ""
|
current_verifier = ""
|
||||||
if _use_uuid:
|
if _use_uuid:
|
||||||
try:
|
try:
|
||||||
with open(self._verifier_path, "r", encoding="utf-8") as f:
|
with self._verifier_path.open("r", encoding="utf-8") as f:
|
||||||
current_verifier = f.read()
|
current_verifier = f.read()
|
||||||
except BaseException:
|
except BaseException:
|
||||||
pass
|
pass
|
||||||
@@ -1065,7 +1101,7 @@ def _read(self):
|
|||||||
self._state_is_inconsistent = False
|
self._state_is_inconsistent = False
|
||||||
return
|
return
|
||||||
elif self.is_upstream:
|
elif self.is_upstream:
|
||||||
tty.warn("upstream not found: {0}".format(self._index_path))
|
tty.warn(f"upstream not found: {self._index_path}")
|
||||||
|
|
||||||
def _add(
|
def _add(
|
||||||
self,
|
self,
|
||||||
@@ -1522,7 +1558,12 @@ def _query(
|
|||||||
# If we did fine something, the query spec can't be virtual b/c we matched an actual
|
# If we did fine something, the query spec can't be virtual b/c we matched an actual
|
||||||
# package installation, so skip the virtual check entirely. If we *didn't* find anything,
|
# package installation, so skip the virtual check entirely. If we *didn't* find anything,
|
||||||
# check all the deferred specs *if* the query is virtual.
|
# check all the deferred specs *if* the query is virtual.
|
||||||
if not results and query_spec is not None and deferred and query_spec.virtual:
|
if (
|
||||||
|
not results
|
||||||
|
and query_spec is not None
|
||||||
|
and deferred
|
||||||
|
and spack.repo.PATH.is_virtual(query_spec.name)
|
||||||
|
):
|
||||||
results = [spec for spec in deferred if spec.satisfies(query_spec)]
|
results = [spec for spec in deferred if spec.satisfies(query_spec)]
|
||||||
|
|
||||||
return results
|
return results
|
||||||
@@ -1681,7 +1722,7 @@ def query(
|
|||||||
)
|
)
|
||||||
|
|
||||||
results = list(local_results) + list(x for x in upstream_results if x not in local_results)
|
results = list(local_results) + list(x for x in upstream_results if x not in local_results)
|
||||||
results.sort()
|
results.sort() # type: ignore[call-overload]
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def query_one(
|
def query_one(
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
import glob
|
import glob
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
import collections
|
import collections
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
@@ -260,6 +259,8 @@ def detect_specs(
|
|||||||
)
|
)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
from spack.repo import PATH as repo_path
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for candidate_path, items_in_prefix in _group_by_prefix(
|
for candidate_path, items_in_prefix in _group_by_prefix(
|
||||||
llnl.util.lang.dedupe(paths)
|
llnl.util.lang.dedupe(paths)
|
||||||
@@ -306,7 +307,10 @@ def detect_specs(
|
|||||||
|
|
||||||
resolved_specs[spec] = candidate_path
|
resolved_specs[spec] = candidate_path
|
||||||
try:
|
try:
|
||||||
spec.validate_detection()
|
# Validate the spec calling a package specific method
|
||||||
|
pkg_cls = repo_path.get_pkg_class(spec.name)
|
||||||
|
validate_fn = getattr(pkg_cls, "validate_detected_spec", lambda x, y: None)
|
||||||
|
validate_fn(spec, spec.extra_attributes)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = (
|
msg = (
|
||||||
f'"{spec}" has been detected on the system but will '
|
f'"{spec}" has been detected on the system but will '
|
||||||
|
@@ -32,7 +32,7 @@ class OpenMpi(Package):
|
|||||||
"""
|
"""
|
||||||
import collections
|
import collections
|
||||||
import collections.abc
|
import collections.abc
|
||||||
import os.path
|
import os
|
||||||
import re
|
import re
|
||||||
from typing import Any, Callable, List, Optional, Tuple, Type, Union
|
from typing import Any, Callable, List, Optional, Tuple, Type, Union
|
||||||
|
|
||||||
|
@@ -581,7 +581,7 @@ def _error_on_nonempty_view_dir(new_root):
|
|||||||
# Check if the target path lexists
|
# Check if the target path lexists
|
||||||
try:
|
try:
|
||||||
st = os.lstat(new_root)
|
st = os.lstat(new_root)
|
||||||
except (IOError, OSError):
|
except OSError:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Empty directories are fine
|
# Empty directories are fine
|
||||||
@@ -861,7 +861,7 @@ def regenerate(self, concrete_roots: List[Spec]) -> None:
|
|||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(old_root)
|
shutil.rmtree(old_root)
|
||||||
except (IOError, OSError) as e:
|
except OSError as e:
|
||||||
msg = "Failed to remove old view at %s\n" % old_root
|
msg = "Failed to remove old view at %s\n" % old_root
|
||||||
msg += str(e)
|
msg += str(e)
|
||||||
tty.warn(msg)
|
tty.warn(msg)
|
||||||
@@ -2554,7 +2554,7 @@ def is_latest_format(manifest):
|
|||||||
try:
|
try:
|
||||||
with open(manifest, encoding="utf-8") as f:
|
with open(manifest, encoding="utf-8") as f:
|
||||||
data = syaml.load(f)
|
data = syaml.load(f)
|
||||||
except (OSError, IOError):
|
except OSError:
|
||||||
return True
|
return True
|
||||||
top_level_key = _top_level_key(data)
|
top_level_key = _top_level_key(data)
|
||||||
changed = spack.schema.env.update(data[top_level_key])
|
changed = spack.schema.env.update(data[top_level_key])
|
||||||
@@ -2634,6 +2634,32 @@ def _ensure_env_dir():
|
|||||||
|
|
||||||
shutil.copy(envfile, target_manifest)
|
shutil.copy(envfile, target_manifest)
|
||||||
|
|
||||||
|
# Copy relative path includes that live inside the environment dir
|
||||||
|
try:
|
||||||
|
manifest = EnvironmentManifestFile(environment_dir)
|
||||||
|
except Exception:
|
||||||
|
# error handling for bad manifests is handled on other code paths
|
||||||
|
return
|
||||||
|
|
||||||
|
includes = manifest[TOP_LEVEL_KEY].get("include", [])
|
||||||
|
for include in includes:
|
||||||
|
if os.path.isabs(include):
|
||||||
|
continue
|
||||||
|
|
||||||
|
abspath = pathlib.Path(os.path.normpath(environment_dir / include))
|
||||||
|
common_path = pathlib.Path(os.path.commonpath([environment_dir, abspath]))
|
||||||
|
if common_path != environment_dir:
|
||||||
|
tty.debug(f"Will not copy relative include from outside environment: {include}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
orig_abspath = os.path.normpath(envfile.parent / include)
|
||||||
|
if not os.path.exists(orig_abspath):
|
||||||
|
tty.warn(f"Included file does not exist; will not copy: '{include}'")
|
||||||
|
continue
|
||||||
|
|
||||||
|
fs.touchp(abspath)
|
||||||
|
shutil.copy(orig_abspath, abspath)
|
||||||
|
|
||||||
|
|
||||||
class EnvironmentManifestFile(collections.abc.Mapping):
|
class EnvironmentManifestFile(collections.abc.Mapping):
|
||||||
"""Manages the in-memory representation of a manifest file, and its synchronization
|
"""Manages the in-memory representation of a manifest file, and its synchronization
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.repo
|
import spack.repo
|
||||||
|
import spack.schema.environment
|
||||||
import spack.store
|
import spack.store
|
||||||
from spack.util.environment import EnvironmentModifications
|
from spack.util.environment import EnvironmentModifications
|
||||||
|
|
||||||
@@ -156,6 +157,11 @@ def activate(
|
|||||||
# MANPATH, PYTHONPATH, etc. All variables that end in PATH (case-sensitive)
|
# MANPATH, PYTHONPATH, etc. All variables that end in PATH (case-sensitive)
|
||||||
# become PATH variables.
|
# become PATH variables.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
env_vars_yaml = env.manifest.configuration.get("env_vars", None)
|
||||||
|
if env_vars_yaml:
|
||||||
|
env_mods.extend(spack.schema.environment.parse(env_vars_yaml))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if view and env.has_view(view):
|
if view and env.has_view(view):
|
||||||
with spack.store.STORE.db.read_transaction():
|
with spack.store.STORE.db.read_transaction():
|
||||||
@@ -189,6 +195,10 @@ def deactivate() -> EnvironmentModifications:
|
|||||||
if active is None:
|
if active is None:
|
||||||
return env_mods
|
return env_mods
|
||||||
|
|
||||||
|
env_vars_yaml = active.manifest.configuration.get("env_vars", None)
|
||||||
|
if env_vars_yaml:
|
||||||
|
env_mods.extend(spack.schema.environment.parse(env_vars_yaml).reversed())
|
||||||
|
|
||||||
active_view = os.getenv(ev.spack_env_view_var)
|
active_view = os.getenv(ev.spack_env_view_var)
|
||||||
|
|
||||||
if active_view and active.has_view(active_view):
|
if active_view and active.has_view(active_view):
|
||||||
|
@@ -187,7 +187,7 @@ def path_for_extension(target_name: str, *, paths: List[str]) -> str:
|
|||||||
if name == target_name:
|
if name == target_name:
|
||||||
return path
|
return path
|
||||||
else:
|
else:
|
||||||
raise IOError('extension "{0}" not found'.format(target_name))
|
raise OSError('extension "{0}" not found'.format(target_name))
|
||||||
|
|
||||||
|
|
||||||
def get_module(cmd_name):
|
def get_module(cmd_name):
|
||||||
|
@@ -25,7 +25,6 @@
|
|||||||
import functools
|
import functools
|
||||||
import http.client
|
import http.client
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import urllib.error
|
import urllib.error
|
||||||
@@ -321,9 +320,15 @@ def _fetch_urllib(self, url):
|
|||||||
|
|
||||||
request = urllib.request.Request(url, headers={"User-Agent": web_util.SPACK_USER_AGENT})
|
request = urllib.request.Request(url, headers={"User-Agent": web_util.SPACK_USER_AGENT})
|
||||||
|
|
||||||
|
if os.path.lexists(save_file):
|
||||||
|
os.remove(save_file)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = web_util.urlopen(request)
|
response = web_util.urlopen(request)
|
||||||
except (TimeoutError, urllib.error.URLError) as e:
|
tty.msg(f"Fetching {url}")
|
||||||
|
with open(save_file, "wb") as f:
|
||||||
|
shutil.copyfileobj(response, f)
|
||||||
|
except OSError as e:
|
||||||
# clean up archive on failure.
|
# clean up archive on failure.
|
||||||
if self.archive_file:
|
if self.archive_file:
|
||||||
os.remove(self.archive_file)
|
os.remove(self.archive_file)
|
||||||
@@ -331,14 +336,6 @@ def _fetch_urllib(self, url):
|
|||||||
os.remove(save_file)
|
os.remove(save_file)
|
||||||
raise FailedDownloadError(e) from e
|
raise FailedDownloadError(e) from e
|
||||||
|
|
||||||
tty.msg(f"Fetching {url}")
|
|
||||||
|
|
||||||
if os.path.lexists(save_file):
|
|
||||||
os.remove(save_file)
|
|
||||||
|
|
||||||
with open(save_file, "wb") as f:
|
|
||||||
shutil.copyfileobj(response, f)
|
|
||||||
|
|
||||||
# Save the redirected URL for error messages. Sometimes we're redirected to an arbitrary
|
# Save the redirected URL for error messages. Sometimes we're redirected to an arbitrary
|
||||||
# mirror that is broken, leading to spurious download failures. In that case it's helpful
|
# mirror that is broken, leading to spurious download failures. In that case it's helpful
|
||||||
# for users to know which URL was actually fetched.
|
# for users to know which URL was actually fetched.
|
||||||
@@ -535,11 +532,16 @@ def __init__(self, *, url: str, checksum: Optional[str] = None, **kwargs):
|
|||||||
@_needs_stage
|
@_needs_stage
|
||||||
def fetch(self):
|
def fetch(self):
|
||||||
file = self.stage.save_filename
|
file = self.stage.save_filename
|
||||||
tty.msg(f"Fetching {self.url}")
|
|
||||||
|
if os.path.lexists(file):
|
||||||
|
os.remove(file)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = self._urlopen(self.url)
|
response = self._urlopen(self.url)
|
||||||
except (TimeoutError, urllib.error.URLError) as e:
|
tty.msg(f"Fetching {self.url}")
|
||||||
|
with open(file, "wb") as f:
|
||||||
|
shutil.copyfileobj(response, f)
|
||||||
|
except OSError as e:
|
||||||
# clean up archive on failure.
|
# clean up archive on failure.
|
||||||
if self.archive_file:
|
if self.archive_file:
|
||||||
os.remove(self.archive_file)
|
os.remove(self.archive_file)
|
||||||
@@ -547,12 +549,6 @@ def fetch(self):
|
|||||||
os.remove(file)
|
os.remove(file)
|
||||||
raise FailedDownloadError(e) from e
|
raise FailedDownloadError(e) from e
|
||||||
|
|
||||||
if os.path.lexists(file):
|
|
||||||
os.remove(file)
|
|
||||||
|
|
||||||
with open(file, "wb") as f:
|
|
||||||
shutil.copyfileobj(response, f)
|
|
||||||
|
|
||||||
|
|
||||||
class VCSFetchStrategy(FetchStrategy):
|
class VCSFetchStrategy(FetchStrategy):
|
||||||
"""Superclass for version control system fetch strategies.
|
"""Superclass for version control system fetch strategies.
|
||||||
|
@@ -35,7 +35,6 @@
|
|||||||
|
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.directory_layout
|
import spack.directory_layout
|
||||||
import spack.paths
|
|
||||||
import spack.projections
|
import spack.projections
|
||||||
import spack.relocate
|
import spack.relocate
|
||||||
import spack.schema.projections
|
import spack.schema.projections
|
||||||
@@ -44,7 +43,6 @@
|
|||||||
import spack.util.spack_json as s_json
|
import spack.util.spack_json as s_json
|
||||||
import spack.util.spack_yaml as s_yaml
|
import spack.util.spack_yaml as s_yaml
|
||||||
from spack.error import SpackError
|
from spack.error import SpackError
|
||||||
from spack.hooks import sbang
|
|
||||||
|
|
||||||
__all__ = ["FilesystemView", "YamlFilesystemView"]
|
__all__ = ["FilesystemView", "YamlFilesystemView"]
|
||||||
|
|
||||||
@@ -91,16 +89,10 @@ def view_copy(
|
|||||||
if stat.S_ISLNK(src_stat.st_mode):
|
if stat.S_ISLNK(src_stat.st_mode):
|
||||||
spack.relocate.relocate_links(links=[dst], prefix_to_prefix=prefix_to_projection)
|
spack.relocate.relocate_links(links=[dst], prefix_to_prefix=prefix_to_projection)
|
||||||
elif spack.relocate.is_binary(dst):
|
elif spack.relocate.is_binary(dst):
|
||||||
spack.relocate.relocate_text_bin(binaries=[dst], prefixes=prefix_to_projection)
|
spack.relocate.relocate_text_bin(binaries=[dst], prefix_to_prefix=prefix_to_projection)
|
||||||
else:
|
else:
|
||||||
prefix_to_projection[spack.store.STORE.layout.root] = view._root
|
prefix_to_projection[spack.store.STORE.layout.root] = view._root
|
||||||
|
spack.relocate.relocate_text(files=[dst], prefix_to_prefix=prefix_to_projection)
|
||||||
# This is vestigial code for the *old* location of sbang.
|
|
||||||
prefix_to_projection[f"#!/bin/bash {spack.paths.spack_root}/bin/sbang"] = (
|
|
||||||
sbang.sbang_shebang_line()
|
|
||||||
)
|
|
||||||
|
|
||||||
spack.relocate.relocate_text(files=[dst], prefixes=prefix_to_projection)
|
|
||||||
|
|
||||||
# The os module on Windows does not have a chown function.
|
# The os module on Windows does not have a chown function.
|
||||||
if sys.platform != "win32":
|
if sys.platform != "win32":
|
||||||
@@ -435,7 +427,7 @@ def needs_file(spec, file):
|
|||||||
try:
|
try:
|
||||||
with open(manifest_file, "r", encoding="utf-8") as f:
|
with open(manifest_file, "r", encoding="utf-8") as f:
|
||||||
manifest = s_json.load(f)
|
manifest = s_json.load(f)
|
||||||
except (OSError, IOError):
|
except OSError:
|
||||||
# if we can't load it, assume it doesn't know about the file.
|
# if we can't load it, assume it doesn't know about the file.
|
||||||
manifest = {}
|
manifest = {}
|
||||||
return test_path in manifest
|
return test_path in manifest
|
||||||
@@ -839,7 +831,7 @@ def get_spec_from_file(filename):
|
|||||||
try:
|
try:
|
||||||
with open(filename, "r", encoding="utf-8") as f:
|
with open(filename, "r", encoding="utf-8") as f:
|
||||||
return spack.spec.Spec.from_yaml(f)
|
return spack.spec.Spec.from_yaml(f)
|
||||||
except IOError:
|
except OSError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@@ -42,10 +42,10 @@
|
|||||||
import llnl.util.tty.color
|
import llnl.util.tty.color
|
||||||
|
|
||||||
import spack.deptypes as dt
|
import spack.deptypes as dt
|
||||||
import spack.repo
|
|
||||||
import spack.spec
|
import spack.spec
|
||||||
import spack.tengine
|
import spack.tengine
|
||||||
import spack.traverse
|
import spack.traverse
|
||||||
|
from spack.solver.input_analysis import create_graph_analyzer
|
||||||
|
|
||||||
|
|
||||||
def find(seq, predicate):
|
def find(seq, predicate):
|
||||||
@@ -537,10 +537,11 @@ def edge_entry(self, edge):
|
|||||||
|
|
||||||
def _static_edges(specs, depflag):
|
def _static_edges(specs, depflag):
|
||||||
for spec in specs:
|
for spec in specs:
|
||||||
pkg_cls = spack.repo.PATH.get_pkg_class(spec.name)
|
*_, edges = create_graph_analyzer().possible_dependencies(
|
||||||
possible = pkg_cls.possible_dependencies(expand_virtuals=True, depflag=depflag)
|
spec.name, expand_virtuals=True, allowed_deps=depflag
|
||||||
|
)
|
||||||
|
|
||||||
for parent_name, dependencies in possible.items():
|
for parent_name, dependencies in edges.items():
|
||||||
for dependency_name in dependencies:
|
for dependency_name in dependencies:
|
||||||
yield spack.spec.DependencySpec(
|
yield spack.spec.DependencySpec(
|
||||||
spack.spec.Spec(parent_name),
|
spack.spec.Spec(parent_name),
|
||||||
|
@@ -26,7 +26,7 @@ def is_shared_library_elf(filepath):
|
|||||||
with open(filepath, "rb") as f:
|
with open(filepath, "rb") as f:
|
||||||
elf = parse_elf(f, interpreter=True, dynamic_section=True)
|
elf = parse_elf(f, interpreter=True, dynamic_section=True)
|
||||||
return elf.has_pt_dynamic and (elf.has_soname or not elf.has_pt_interp)
|
return elf.has_pt_dynamic and (elf.has_soname or not elf.has_pt_interp)
|
||||||
except (IOError, OSError, ElfParsingError):
|
except (OSError, ElfParsingError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@@ -166,7 +166,7 @@ def filter_shebangs_in_directory(directory, filenames=None):
|
|||||||
# Only look at executable, non-symlink files.
|
# Only look at executable, non-symlink files.
|
||||||
try:
|
try:
|
||||||
st = os.lstat(path)
|
st = os.lstat(path)
|
||||||
except (IOError, OSError):
|
except OSError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if stat.S_ISLNK(st.st_mode) or stat.S_ISDIR(st.st_mode) or not st.st_mode & is_exe:
|
if stat.S_ISLNK(st.st_mode) or stat.S_ISDIR(st.st_mode) or not st.st_mode & is_exe:
|
||||||
|
@@ -566,7 +566,7 @@ def copy_test_files(pkg: Pb, test_spec: spack.spec.Spec):
|
|||||||
|
|
||||||
# copy test data into test stage data dir
|
# copy test data into test stage data dir
|
||||||
try:
|
try:
|
||||||
pkg_cls = test_spec.package_class
|
pkg_cls = spack.repo.PATH.get_pkg_class(test_spec.fullname)
|
||||||
except spack.repo.UnknownPackageError:
|
except spack.repo.UnknownPackageError:
|
||||||
tty.debug(f"{test_spec.name}: skipping test data copy since no package class found")
|
tty.debug(f"{test_spec.name}: skipping test data copy since no package class found")
|
||||||
return
|
return
|
||||||
@@ -623,7 +623,7 @@ def test_functions(
|
|||||||
vpkgs = virtuals(pkg)
|
vpkgs = virtuals(pkg)
|
||||||
for vname in vpkgs:
|
for vname in vpkgs:
|
||||||
try:
|
try:
|
||||||
classes.append((Spec(vname)).package_class)
|
classes.append(spack.repo.PATH.get_pkg_class(vname))
|
||||||
except spack.repo.UnknownPackageError:
|
except spack.repo.UnknownPackageError:
|
||||||
tty.debug(f"{vname}: virtual does not appear to have a package file")
|
tty.debug(f"{vname}: virtual does not appear to have a package file")
|
||||||
|
|
||||||
@@ -668,7 +668,7 @@ def process_test_parts(pkg: Pb, test_specs: List[spack.spec.Spec], verbose: bool
|
|||||||
|
|
||||||
# grab test functions associated with the spec, which may be virtual
|
# grab test functions associated with the spec, which may be virtual
|
||||||
try:
|
try:
|
||||||
tests = test_functions(spec.package_class)
|
tests = test_functions(spack.repo.PATH.get_pkg_class(spec.fullname))
|
||||||
except spack.repo.UnknownPackageError:
|
except spack.repo.UnknownPackageError:
|
||||||
# Some virtuals don't have a package so we don't want to report
|
# Some virtuals don't have a package so we don't want to report
|
||||||
# them as not having tests when that isn't appropriate.
|
# them as not having tests when that isn't appropriate.
|
||||||
|
@@ -275,7 +275,7 @@ def _do_fake_install(pkg: "spack.package_base.PackageBase") -> None:
|
|||||||
fs.mkdirp(pkg.prefix.bin)
|
fs.mkdirp(pkg.prefix.bin)
|
||||||
fs.touch(os.path.join(pkg.prefix.bin, command))
|
fs.touch(os.path.join(pkg.prefix.bin, command))
|
||||||
if sys.platform != "win32":
|
if sys.platform != "win32":
|
||||||
chmod = which("chmod")
|
chmod = which("chmod", required=True)
|
||||||
chmod("+x", os.path.join(pkg.prefix.bin, command))
|
chmod("+x", os.path.join(pkg.prefix.bin, command))
|
||||||
|
|
||||||
# Install fake header file
|
# Install fake header file
|
||||||
@@ -814,7 +814,7 @@ def get_depflags(self, pkg: "spack.package_base.PackageBase") -> int:
|
|||||||
# Include build dependencies if pkg is going to be built from sources, or
|
# Include build dependencies if pkg is going to be built from sources, or
|
||||||
# if build deps are explicitly requested.
|
# if build deps are explicitly requested.
|
||||||
if include_build_deps or not (
|
if include_build_deps or not (
|
||||||
cache_only or pkg.spec.installed and not pkg.spec.dag_hash() in self.overwrite
|
cache_only or pkg.spec.installed and pkg.spec.dag_hash() not in self.overwrite
|
||||||
):
|
):
|
||||||
depflag |= dt.BUILD
|
depflag |= dt.BUILD
|
||||||
if self.run_tests(pkg):
|
if self.run_tests(pkg):
|
||||||
@@ -2436,11 +2436,7 @@ def _real_install(self) -> None:
|
|||||||
# DEBUGGING TIP - to debug this section, insert an IPython
|
# DEBUGGING TIP - to debug this section, insert an IPython
|
||||||
# embed here, and run the sections below without log capture
|
# embed here, and run the sections below without log capture
|
||||||
log_contextmanager = log_output(
|
log_contextmanager = log_output(
|
||||||
log_file,
|
log_file, self.echo, True, filter_fn=self.filter_fn
|
||||||
self.echo,
|
|
||||||
True,
|
|
||||||
env=self.unmodified_env,
|
|
||||||
filter_fn=self.filter_fn,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with log_contextmanager as logger:
|
with log_contextmanager as logger:
|
||||||
|
@@ -14,7 +14,6 @@
|
|||||||
import io
|
import io
|
||||||
import operator
|
import operator
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import pstats
|
import pstats
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
@@ -164,7 +163,7 @@ def format_help_sections(self, level):
|
|||||||
# lazily add all commands to the parser when needed.
|
# lazily add all commands to the parser when needed.
|
||||||
add_all_commands(self)
|
add_all_commands(self)
|
||||||
|
|
||||||
"""Print help on subcommands in neatly formatted sections."""
|
# Print help on subcommands in neatly formatted sections.
|
||||||
formatter = self._get_formatter()
|
formatter = self._get_formatter()
|
||||||
|
|
||||||
# Create a list of subcommand actions. Argparse internals are nasty!
|
# Create a list of subcommand actions. Argparse internals are nasty!
|
||||||
@@ -729,7 +728,7 @@ def _compatible_sys_types():
|
|||||||
with the current host.
|
with the current host.
|
||||||
"""
|
"""
|
||||||
host_platform = spack.platforms.host()
|
host_platform = spack.platforms.host()
|
||||||
host_os = str(host_platform.operating_system("default_os"))
|
host_os = str(host_platform.default_operating_system())
|
||||||
host_target = archspec.cpu.host()
|
host_target = archspec.cpu.host()
|
||||||
compatible_targets = [host_target] + host_target.ancestors
|
compatible_targets = [host_target] + host_target.ancestors
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user