Compare commits
221 Commits
libgit2-25
...
double-quo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6935d6c18 | ||
|
|
6cc44e4fdf | ||
|
|
952be9fc96 | ||
|
|
4031343e44 | ||
|
|
0c0a41a81a | ||
|
|
97abcda9cc | ||
|
|
423e732b22 | ||
|
|
05990fb2ec | ||
|
|
6a4a1e9f72 | ||
|
|
1c10ce6047 | ||
|
|
6f33f64ce5 | ||
|
|
801cb16131 | ||
|
|
e91b7d1732 | ||
|
|
ab58d2720c | ||
|
|
c72f3dc27e | ||
|
|
d1470f76c7 | ||
|
|
84079e10cf | ||
|
|
88c9c6d89d | ||
|
|
4f4da90513 | ||
|
|
3e9104c9ca | ||
|
|
3a9be9fd2f | ||
|
|
0275b64b81 | ||
|
|
622a5cd1bf | ||
|
|
b0c016ae7d | ||
|
|
f78e88c973 | ||
|
|
d8d75cff9f | ||
|
|
f198e9a0b3 | ||
|
|
439af1dca1 | ||
|
|
77990e7cca | ||
|
|
3bac0d7aa2 | ||
|
|
36419a6ccb | ||
|
|
3ba51bf61b | ||
|
|
209f413e80 | ||
|
|
b8d32388bc | ||
|
|
eb53e61e08 | ||
|
|
60f09928d1 | ||
|
|
43a183120a | ||
|
|
0c786f3a3c | ||
|
|
504c5e7cf9 | ||
|
|
5d066386b5 | ||
|
|
c7b61f3d13 | ||
|
|
d689b764f3 | ||
|
|
487c6b6c46 | ||
|
|
28fac9fe4d | ||
|
|
2594e417b5 | ||
|
|
76ed967f79 | ||
|
|
327e8babf7 | ||
|
|
d5d4bafc2a | ||
|
|
bd11043c67 | ||
|
|
dbfe6318b3 | ||
|
|
484f40fc64 | ||
|
|
43fc6c314d | ||
|
|
2bbec7d573 | ||
|
|
385d7e77bd | ||
|
|
67f6a24171 | ||
|
|
8cdeab8f2e | ||
|
|
ed176cb42e | ||
|
|
3ff8d0ece4 | ||
|
|
c9fe290b30 | ||
|
|
48c800f7ef | ||
|
|
79dcc094b0 | ||
|
|
be28ad92fd | ||
|
|
a2d6a69d45 | ||
|
|
4307420c44 | ||
|
|
ec0b270c6c | ||
|
|
3f8474a62f | ||
|
|
c7e1c612eb | ||
|
|
a812b6c6e6 | ||
|
|
59a566db13 | ||
|
|
eb654acdd1 | ||
|
|
7cd3252946 | ||
|
|
9b9446e860 | ||
|
|
6c4d2a7d11 | ||
|
|
152e7e48c1 | ||
|
|
ea4854fda1 | ||
|
|
d3ff01cb2e | ||
|
|
a835d6ad2a | ||
|
|
ec3c93f17f | ||
|
|
6d0f4fa666 | ||
|
|
b2ead92791 | ||
|
|
50407ab63e | ||
|
|
7357a654de | ||
|
|
c4906741a1 | ||
|
|
ac36d74b66 | ||
|
|
d17bfe3866 | ||
|
|
437b9b9879 | ||
|
|
5caebab63a | ||
|
|
620a6947ab | ||
|
|
294acfd807 | ||
|
|
19d83d2605 | ||
|
|
70b9fbd76c | ||
|
|
40b25153b8 | ||
|
|
09755e696a | ||
|
|
fa380e0991 | ||
|
|
f7de5b326a | ||
|
|
533cced249 | ||
|
|
8b167ea89b | ||
|
|
76bd600302 | ||
|
|
b975f719b1 | ||
|
|
063cdb5508 | ||
|
|
72dbd43882 | ||
|
|
fb989bd93f | ||
|
|
b309826a48 | ||
|
|
bed0570629 | ||
|
|
ef6dbe76dc | ||
|
|
dfac44cdfb | ||
|
|
36f4e290d0 | ||
|
|
bd0b338e15 | ||
|
|
b3dfe37aea | ||
|
|
87d3c3ba1a | ||
|
|
1e42e55fb4 | ||
|
|
e704b8eeed | ||
|
|
6d65f8eea2 | ||
|
|
f4989b118b | ||
|
|
2de742155a | ||
|
|
09d6847490 | ||
|
|
53af1119fb | ||
|
|
68d2292f3a | ||
|
|
16f0279d4f | ||
|
|
8165419a0c | ||
|
|
7721fa6df4 | ||
|
|
cb5d97a607 | ||
|
|
436bc1f39e | ||
|
|
ae4ed24257 | ||
|
|
fcf3bdcac8 | ||
|
|
4b97f1130a | ||
|
|
614e143a20 | ||
|
|
77982c55b2 | ||
|
|
acacdf87b4 | ||
|
|
d5b6e1a0fc | ||
|
|
3511a919b4 | ||
|
|
f6aa8c0486 | ||
|
|
cd5cac0c40 | ||
|
|
958866b9a6 | ||
|
|
d07c24f4c8 | ||
|
|
95da93c05b | ||
|
|
bae1ca257a | ||
|
|
f8141a2c26 | ||
|
|
bdeaf976bd | ||
|
|
2cc0b1b404 | ||
|
|
cdba2534cf | ||
|
|
5446d6345f | ||
|
|
b115c90043 | ||
|
|
13b896a188 | ||
|
|
5462c5eedd | ||
|
|
aec59a973a | ||
|
|
8642c0a9a2 | ||
|
|
653d701300 | ||
|
|
8d881ee3a3 | ||
|
|
2872c8ede0 | ||
|
|
57f526ecda | ||
|
|
1f2a994fb9 | ||
|
|
70e56a41ce | ||
|
|
0e81a35881 | ||
|
|
a235b454cc | ||
|
|
94c3bb3e4c | ||
|
|
30dbc7ee0c | ||
|
|
19ab65c9d7 | ||
|
|
805496657d | ||
|
|
e95503cf9a | ||
|
|
1bcbe652fb | ||
|
|
f98bc8f41f | ||
|
|
af7127459d | ||
|
|
91cdd88714 | ||
|
|
1b5af49fd0 | ||
|
|
292bd390af | ||
|
|
3645671570 | ||
|
|
c7f17358fc | ||
|
|
ddc3fba9fb | ||
|
|
af0ac14021 | ||
|
|
abb7d2a96e | ||
|
|
7ff3cc65e4 | ||
|
|
918c1a9e58 | ||
|
|
091c0a97e1 | ||
|
|
f2253a00bc | ||
|
|
a5eba9a354 | ||
|
|
295ad5c05f | ||
|
|
204749270b | ||
|
|
f5390e76e4 | ||
|
|
533db37ebc | ||
|
|
d00c419ed6 | ||
|
|
87a2ce492f | ||
|
|
2150d7a754 | ||
|
|
d1f750a714 | ||
|
|
af1db7774f | ||
|
|
90ba96a3d6 | ||
|
|
750306234d | ||
|
|
2d728f0c56 | ||
|
|
5ffc9fd253 | ||
|
|
68a5110fb9 | ||
|
|
182ae393d1 | ||
|
|
4e64dea21b | ||
|
|
060a354f22 | ||
|
|
496e43ec72 | ||
|
|
7a60f1429f | ||
|
|
65fbb4d975 | ||
|
|
070e8ee590 | ||
|
|
46b5d2e739 | ||
|
|
709a73e7ae | ||
|
|
accb564889 | ||
|
|
a786c9eedb | ||
|
|
3bf8c76072 | ||
|
|
8c113f80f3 | ||
|
|
ac9d2a5b06 | ||
|
|
0c53c88367 | ||
|
|
d6fc64ac38 | ||
|
|
cb5b0c30aa | ||
|
|
1fa235b77c | ||
|
|
e2040aecac | ||
|
|
c5f348db95 | ||
|
|
4f1c8f62c3 | ||
|
|
80b1d7b87a | ||
|
|
9c04c629e5 | ||
|
|
40f600644d | ||
|
|
261f674a25 | ||
|
|
08e218eb0b | ||
|
|
2349c3dbde | ||
|
|
f6aeca0522 | ||
|
|
bc6b9cef51 | ||
|
|
d4fd5c222d | ||
|
|
a38c7eb64e |
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
with:
|
||||
app-id: ${{ vars.CI_APP_ID }}
|
||||
private-key: ${{ secrets.CI_APP_PRIVATE_KEY }}
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
# required to find all branches
|
||||
|
||||
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
eval:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
name: pre-commit checks
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
with:
|
||||
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
name: installer test ${{ matrix.scenario }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Download installer tarball
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
@@ -174,7 +174,7 @@ jobs:
|
||||
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
|
||||
TARBALL_PATH="$(find "$GITHUB_WORKSPACE/out" -name 'nix*.tar.xz' -print | head -n 1)"
|
||||
echo "tarball-path=file://$TARBALL_PATH" >> "$GITHUB_OUTPUT"
|
||||
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31.8.2
|
||||
- uses: cachix/install-nix-action@0b0e072294b088b73964f1d72dfdac0951439dbd # v31.8.4
|
||||
if: ${{ !matrix.experimental-installer }}
|
||||
with:
|
||||
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
|
||||
@@ -227,7 +227,7 @@ jobs:
|
||||
github.ref_name == 'master'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
@@ -276,14 +276,14 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout nix
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout flake-regressions
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: NixOS/flake-regressions
|
||||
path: flake-regressions
|
||||
- name: Checkout flake-regressions-data
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: NixOS/flake-regressions-data
|
||||
path: flake-regressions/tests
|
||||
@@ -303,7 +303,7 @@ jobs:
|
||||
github.event_name == 'push' &&
|
||||
github.ref_name == 'master'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
|
||||
@@ -88,7 +88,7 @@ manual = custom_target(
|
||||
@0@ @INPUT0@ @CURRENT_SOURCE_DIR@ > @DEPFILE@
|
||||
@0@ @INPUT1@ summary @2@ < @CURRENT_SOURCE_DIR@/source/SUMMARY.md.in > @2@/source/SUMMARY.md
|
||||
sed -e 's|@version@|@3@|g' < @INPUT2@ > @2@/book.toml
|
||||
@4@ -r -L --include='*.md' @CURRENT_SOURCE_DIR@/ @2@/
|
||||
@4@ -r -L --exclude='*.drv' --include='*.md' @CURRENT_SOURCE_DIR@/ @2@/
|
||||
(cd @2@; RUST_LOG=warn @1@ build -d @2@ 3>&2 2>&1 1>&3) | { grep -Fv "because fragment resolution isn't implemented" || :; } 3>&2 2>&1 1>&3
|
||||
rm -rf @2@/manual
|
||||
mv @2@/html @2@/manual
|
||||
|
||||
@@ -37,14 +37,17 @@ mkMesonDerivation (finalAttrs: {
|
||||
(fileset.unions [
|
||||
../../.version
|
||||
# For example JSON
|
||||
../../src/libutil-tests/data/memory-source-accessor
|
||||
../../src/libutil-tests/data/hash
|
||||
../../src/libstore-tests/data/content-address
|
||||
../../src/libstore-tests/data/store-path
|
||||
../../src/libstore-tests/data/realisation
|
||||
../../src/libstore-tests/data/derivation
|
||||
../../src/libstore-tests/data/derived-path
|
||||
../../src/libstore-tests/data/path-info
|
||||
../../src/libstore-tests/data/nar-info
|
||||
../../src/libstore-tests/data/build-result
|
||||
../../src/libstore-tests/data/dummy-store
|
||||
# Too many different types of files to filter for now
|
||||
../../doc/manual
|
||||
./.
|
||||
@@ -58,8 +61,8 @@ mkMesonDerivation (finalAttrs: {
|
||||
"man"
|
||||
];
|
||||
|
||||
# Hack for sake of the dev shell
|
||||
passthru.externalNativeBuildInputs = [
|
||||
nativeBuildInputs = [
|
||||
nix-cli
|
||||
meson
|
||||
ninja
|
||||
(lib.getBin lowdown-unsandboxed)
|
||||
@@ -78,10 +81,6 @@ mkMesonDerivation (finalAttrs: {
|
||||
changelog-d
|
||||
];
|
||||
|
||||
nativeBuildInputs = finalAttrs.passthru.externalNativeBuildInputs ++ [
|
||||
nix-cli
|
||||
];
|
||||
|
||||
preConfigure = ''
|
||||
chmod u+w ./.version
|
||||
echo ${finalAttrs.version} > ./.version
|
||||
|
||||
@@ -22,7 +22,15 @@ The store path info JSON format has been updated from version 1 to version 2:
|
||||
- New: `"ca": {"method": "nar", "hash": {"algorithm": "sha256", "format": "base64", "hash": "EMIJ+giQ..."}}`
|
||||
- Still `null` values for input-addressed store objects
|
||||
|
||||
Version 1 format is still accepted when reading for backward compatibility.
|
||||
- **Structured hash fields**:
|
||||
|
||||
Hash values (`narHash` and `downloadHash`) are now structured JSON objects instead of strings:
|
||||
|
||||
- Old: `"narHash": "sha256:FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="`
|
||||
- New: `"narHash": {"algorithm": "sha256", "format": "base64", "hash": "FePFYIlM..."}`
|
||||
- Same structure applies to `downloadHash` in NAR info contexts
|
||||
|
||||
Nix currently only produces, and doesn't consume this format.
|
||||
|
||||
**Affected command**: `nix path-info --json`
|
||||
|
||||
|
||||
12
doc/manual/rl-next/libcurl-pausing.md
Normal file
12
doc/manual/rl-next/libcurl-pausing.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
synopsis: Fix "download buffer is full; consider increasing the 'download-buffer-size' setting" warning
|
||||
prs: [14614]
|
||||
issues: [11728]
|
||||
---
|
||||
|
||||
The underlying issue that led to [#11728](https://github.com/NixOS/nix/issues/11728) has been resolved by utilizing
|
||||
[libcurl write pausing functionality](https://curl.se/libcurl/c/curl_easy_pause.html) to control backpressure when unpacking to slow destinations like the git-backed tarball cache. The default value of `download-buffer-size` is now 1 MiB and it's no longer recommended to increase it, since the root cause has been fixed.
|
||||
|
||||
This is expected to improve download performance on fast connections, since previously a single slow download consumer would stall the thread and prevent any other transfers from progressing.
|
||||
|
||||
Many thanks go out to the [Lix project](https://lix.systems/) for the [implementation](https://git.lix.systems/lix-project/lix/commit/4ae6fb5a8f0d456b8d2ba2aaca3712b4e49057fc) that served as inspiration for this change and for triaging libcurl [issues with pausing](https://github.com/curl/curl/issues/19334).
|
||||
21
doc/manual/rl-next/s3-storage-class.md
Normal file
21
doc/manual/rl-next/s3-storage-class.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
synopsis: "S3 binary cache stores now support storage class configuration"
|
||||
prs: [14464]
|
||||
issues: [7015]
|
||||
---
|
||||
|
||||
S3 binary cache stores now support configuring the storage class for uploaded objects via the `storage-class` parameter. This allows users to optimize costs by selecting appropriate storage tiers based on access patterns.
|
||||
|
||||
Example usage:
|
||||
|
||||
```bash
|
||||
# Use Glacier storage for long-term archival
|
||||
nix copy --to 's3://my-bucket?storage-class=GLACIER' /nix/store/...
|
||||
|
||||
# Use Intelligent Tiering for automatic cost optimization
|
||||
nix copy --to 's3://my-bucket?storage-class=INTELLIGENT_TIERING' /nix/store/...
|
||||
```
|
||||
|
||||
The storage class applies to both regular uploads and multipart uploads. When not specified, objects use the bucket's default storage class.
|
||||
|
||||
See the [S3 storage classes documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html) for available storage classes and their characteristics.
|
||||
@@ -29,6 +29,7 @@
|
||||
- [Build Trace](store/build-trace.md)
|
||||
- [Derivation Resolution](store/resolution.md)
|
||||
- [Building](store/building.md)
|
||||
- [Secrets](store/secrets.md)
|
||||
- [Store Types](store/types/index.md)
|
||||
{{#include ./store/types/SUMMARY.md}}
|
||||
- [Appendix: Math notation](store/math-notation.md)
|
||||
@@ -120,14 +121,17 @@
|
||||
- [Architecture and Design](architecture/architecture.md)
|
||||
- [Formats and Protocols](protocols/index.md)
|
||||
- [JSON Formats](protocols/json/index.md)
|
||||
- [File System Object](protocols/json/file-system-object.md)
|
||||
- [Hash](protocols/json/hash.md)
|
||||
- [Content Address](protocols/json/content-address.md)
|
||||
- [Store Path](protocols/json/store-path.md)
|
||||
- [Store Object Info](protocols/json/store-object-info.md)
|
||||
- [Derivation](protocols/json/derivation.md)
|
||||
- [Derivation](protocols/json/derivation/index.md)
|
||||
- [Derivation Options](protocols/json/derivation/options.md)
|
||||
- [Deriving Path](protocols/json/deriving-path.md)
|
||||
- [Build Trace Entry](protocols/json/build-trace-entry.md)
|
||||
- [Build Result](protocols/json/build-result.md)
|
||||
- [Store](protocols/json/store.md)
|
||||
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
|
||||
- [Store Path Specification](protocols/store-path.md)
|
||||
- [Nix Archive (NAR) Format](protocols/nix-archive/index.md)
|
||||
|
||||
@@ -36,7 +36,7 @@ to a temporary location. The tarball must include a single top-level
|
||||
directory containing at least a file named `default.nix`.
|
||||
|
||||
`nix-build` is essentially a wrapper around
|
||||
[`nix-instantiate`](nix-instantiate.md) (to translate a high-level Nix
|
||||
[`nix-instantiate`](./nix-instantiate.md) (to translate a high-level Nix
|
||||
expression to a low-level [store derivation]) and [`nix-store
|
||||
--realise`](@docroot@/command-ref/nix-store/realise.md) (to build the store
|
||||
derivation).
|
||||
@@ -52,8 +52,8 @@ derivation).
|
||||
# Options
|
||||
|
||||
All options not listed here are passed to
|
||||
[`nix-store --realise`](nix-store/realise.md),
|
||||
except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](nix-instantiate.md).
|
||||
[`nix-store --realise`](./nix-store/realise.md),
|
||||
except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](./nix-instantiate.md).
|
||||
|
||||
- <span id="opt-no-out-link">[`--no-out-link`](#opt-no-out-link)<span>
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ standard input.
|
||||
|
||||
- `--add-root` *path*
|
||||
|
||||
See the [corresponding option](nix-store.md) in `nix-store`.
|
||||
See the [corresponding option](./nix-store.md) in `nix-store`.
|
||||
|
||||
- `--parse`
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ In the development shell, set the `mesonBuildType` environment variable to `debu
|
||||
Then, proceed to build Nix as described in [Building Nix](./building.md).
|
||||
This will build Nix with debug symbols, which are essential for effective debugging.
|
||||
|
||||
It is also possible to build without debugging for faster build:
|
||||
It is also possible to build without optimization for faster build:
|
||||
|
||||
```console
|
||||
[nix-shell]$ NIX_HARDENING_ENABLE=$(printLines $NIX_HARDENING_ENABLE | grep -v fortify)
|
||||
|
||||
@@ -137,6 +137,12 @@ $ _NIX_TEST_ACCEPT=1 meson test nix-store-tests -v
|
||||
will regenerate the "golden master" expected result for the `libnixstore` characterisation tests.
|
||||
The characterisation tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything.
|
||||
|
||||
### JSON Schema testing
|
||||
|
||||
In `doc/manual/source/protocols/json/` we have a number of manual pages generated from [JSON Schema](https://json-schema.org/).
|
||||
That JSON schema is tested against the JSON file test data used in [characterisation tests](#characterisation-testing-unit ) for JSON (de)serialization, in `src/json-schema-checks`.
|
||||
Between the JSON (de)serialization testing, and this testing of the same data against the schema, we make sure that the manual, the implementation, and a machine-readable schema are are all in sync.
|
||||
|
||||
### Unit test support libraries
|
||||
|
||||
There are headers and code which are not just used to test the library in question, but also downstream libraries.
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{{#include derivation-v4-fixed.md}}
|
||||
|
||||
<!-- need to convert YAML to JSON first
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for Derivation v3](schema/derivation-v4.json)
|
||||
-->
|
||||
7
doc/manual/source/protocols/json/derivation/index.md
Normal file
7
doc/manual/source/protocols/json/derivation/index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
{{#include ../derivation-v4-fixed.md}}
|
||||
|
||||
<!-- need to convert YAML to JSON first
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for Derivation v4](schema/derivation-v4.json)
|
||||
-->
|
||||
49
doc/manual/source/protocols/json/derivation/options.md
Normal file
49
doc/manual/source/protocols/json/derivation/options.md
Normal file
@@ -0,0 +1,49 @@
|
||||
{{#include ../derivation-options-v1-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Input-addressed derivations
|
||||
|
||||
#### Default options
|
||||
|
||||
```json
|
||||
{{#include ../schema/derivation-options-v1/ia/defaults.json}}
|
||||
```
|
||||
|
||||
#### All options set
|
||||
|
||||
```json
|
||||
{{#include ../schema/derivation-options-v1/ia/all_set.json}}
|
||||
```
|
||||
|
||||
#### Default options (structured attributes)
|
||||
|
||||
```json
|
||||
{{#include ../schema/derivation-options-v1/ia/structuredAttrs_defaults.json}}
|
||||
```
|
||||
|
||||
#### All options set (structured attributes)
|
||||
|
||||
```json
|
||||
{{#include ../schema/derivation-options-v1/ia/structuredAttrs_all_set.json}}
|
||||
```
|
||||
|
||||
### Content-addressed derivations
|
||||
|
||||
#### All options set
|
||||
|
||||
```json
|
||||
{{#include ../schema/derivation-options-v1/ca/all_set.json}}
|
||||
```
|
||||
|
||||
#### All options set (structured attributes)
|
||||
|
||||
```json
|
||||
{{#include ../schema/derivation-options-v1/ca/structuredAttrs_all_set.json}}
|
||||
```
|
||||
|
||||
<!-- need to convert YAML to JSON first
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for Derivation Options v1](schema/derivation-options-v1.json)
|
||||
-->
|
||||
21
doc/manual/source/protocols/json/file-system-object.md
Normal file
21
doc/manual/source/protocols/json/file-system-object.md
Normal file
@@ -0,0 +1,21 @@
|
||||
{{#include file-system-object-v1-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple
|
||||
|
||||
```json
|
||||
{{#include schema/file-system-object-v1/simple.json}}
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
```json
|
||||
{{#include schema/file-system-object-v1/complex.json}}
|
||||
```
|
||||
|
||||
<!-- need to convert YAML to JSON first
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for File System Object v1](schema/file-system-object-v1.json)
|
||||
-->
|
||||
@@ -11,7 +11,8 @@ s/\\`/`/g
|
||||
#
|
||||
# As we have more such relative links, more replacements of this nature
|
||||
# should appear below.
|
||||
s^\(./hash-v1.yaml\)\?#/$defs/algorithm^[JSON format for `Hash`](./hash.html#algorithm)^g
|
||||
s^\(./hash-v1.yaml\)^[JSON format for `Hash`](./hash.html)^g
|
||||
s^\(./content-address-v1.yaml\)\?#/$defs/method^[JSON format for `ContentAddress`](./content-address.html#method)^g
|
||||
s^\(./content-address-v1.yaml\)^[JSON format for `ContentAddress`](./content-address.html)^g
|
||||
s^#/\$defs/\(regular\|symlink\|directory\)^In this schema^g
|
||||
s^\(./hash-v1.yaml\)\?#/$defs/algorithm^[JSON format for `Hash`](@docroot@/protocols/json/hash.html#algorithm)^g
|
||||
s^\(./hash-v1.yaml\)^[JSON format for `Hash`](@docroot@/protocols/json/hash.html)^g
|
||||
s^\(./content-address-v1.yaml\)\?#/$defs/method^[JSON format for `ContentAddress`](@docroot@/protocols/json/content-address.html#method)^g
|
||||
s^\(./content-address-v1.yaml\)^[JSON format for `ContentAddress`](@docroot@/protocols/json/content-address.html)^g
|
||||
|
||||
@@ -9,14 +9,17 @@ json_schema_for_humans = find_program('generate-schema-doc', required : false)
|
||||
json_schema_config = files('json-schema-for-humans-config.yaml')
|
||||
|
||||
schemas = [
|
||||
'file-system-object-v1',
|
||||
'hash-v1',
|
||||
'content-address-v1',
|
||||
'store-path-v1',
|
||||
'store-object-info-v2',
|
||||
'derivation-v4',
|
||||
'derivation-options-v1',
|
||||
'deriving-path-v1',
|
||||
'build-trace-entry-v1',
|
||||
'build-result-v1',
|
||||
'store-v1',
|
||||
]
|
||||
|
||||
schema_files = files()
|
||||
|
||||
@@ -4,71 +4,97 @@ title: Build Trace Entry
|
||||
description: |
|
||||
A record of a successful build outcome for a specific derivation output.
|
||||
|
||||
This schema describes the JSON representation of a [build trace entry](@docroot@/store/build-trace.md) entry.
|
||||
This schema describes the JSON representation of a [build trace entry](@docroot@/store/build-trace.md).
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This JSON format is currently
|
||||
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-ca-derivations)
|
||||
> and subject to change.
|
||||
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- outPath
|
||||
- dependentRealisations
|
||||
- signatures
|
||||
allOf:
|
||||
- "$ref": "#/$defs/key"
|
||||
- "$ref": "#/$defs/value"
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
title: Derivation Output ID
|
||||
pattern: "^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$"
|
||||
description: |
|
||||
Unique identifier for the derivation output that was built.
|
||||
|
||||
Format: `{hash-quotient-drv}!{output-name}`
|
||||
|
||||
- **hash-quotient-drv**: SHA-256 [hash of the quotient derivation](@docroot@/store/derivation/outputs/input-address.md#hash-quotient-drv).
|
||||
Begins with `sha256:`.
|
||||
|
||||
- **output-name**: Name of the specific output (e.g., "out", "dev", "doc")
|
||||
|
||||
Example: `"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo"`
|
||||
|
||||
outPath:
|
||||
"$ref": "store-path-v1.yaml"
|
||||
title: Output Store Path
|
||||
description: |
|
||||
The path to the store object that resulted from building this derivation for the given output name.
|
||||
|
||||
dependentRealisations:
|
||||
type: object
|
||||
title: Underlying Base Build Trace
|
||||
description: |
|
||||
This is for [*derived*](@docroot@/store/build-trace.md#derived) build trace entries to ensure coherence.
|
||||
|
||||
Keys are derivation output IDs (same format as the main `id` field).
|
||||
Values are the store paths that those dependencies resolved to.
|
||||
|
||||
As described in the linked section on derived build trace traces, derived build trace entries must be kept in addition and not instead of the underlying base build entries.
|
||||
This is the set of base build trace entries that this derived build trace is derived from.
|
||||
(The set is also a map since this miniature base build trace must be coherent, mapping each key to a single value.)
|
||||
|
||||
patternProperties:
|
||||
"^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$":
|
||||
$ref: "store-path-v1.yaml"
|
||||
title: Dependent Store Path
|
||||
description: Store path that this dependency resolved to during the build
|
||||
additionalProperties: false
|
||||
|
||||
signatures:
|
||||
type: array
|
||||
title: Build Signatures
|
||||
description: |
|
||||
A set of cryptographic signatures attesting to the authenticity of this build trace entry.
|
||||
items:
|
||||
type: string
|
||||
title: Signature
|
||||
description: A single cryptographic signature
|
||||
|
||||
id: {}
|
||||
outPath: {}
|
||||
dependentRealisations: {}
|
||||
signatures: {}
|
||||
additionalProperties: false
|
||||
|
||||
"$defs":
|
||||
key:
|
||||
title: Build Trace Key
|
||||
description: |
|
||||
A [build trace entry](@docroot@/store/build-trace.md) is a key-value pair.
|
||||
This is the "key" part, refering to a derivation and output.
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
title: Derivation Output ID
|
||||
pattern: "^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$"
|
||||
description: |
|
||||
Unique identifier for the derivation output that was built.
|
||||
|
||||
Format: `{hash-quotient-drv}!{output-name}`
|
||||
|
||||
- **hash-quotient-drv**: SHA-256 [hash of the quotient derivation](@docroot@/store/derivation/outputs/input-address.md#hash-quotient-drv).
|
||||
Begins with `sha256:`.
|
||||
|
||||
- **output-name**: Name of the specific output (e.g., "out", "dev", "doc")
|
||||
|
||||
Example: `"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo"`
|
||||
|
||||
value:
|
||||
title: Build Trace Value
|
||||
description: |
|
||||
A [build trace entry](@docroot@/store/build-trace.md) is a key-value pair.
|
||||
This is the "value" part, describing an output.
|
||||
type: object
|
||||
required:
|
||||
- outPath
|
||||
- dependentRealisations
|
||||
- signatures
|
||||
properties:
|
||||
outPath:
|
||||
"$ref": "store-path-v1.yaml"
|
||||
title: Output Store Path
|
||||
description: |
|
||||
The path to the store object that resulted from building this derivation for the given output name.
|
||||
|
||||
dependentRealisations:
|
||||
type: object
|
||||
title: Underlying Base Build Trace
|
||||
description: |
|
||||
This is for [*derived*](@docroot@/store/build-trace.md#derived) build trace entries to ensure coherence.
|
||||
|
||||
Keys are derivation output IDs (same format as the main `id` field).
|
||||
Values are the store paths that those dependencies resolved to.
|
||||
|
||||
As described in the linked section on derived build trace traces, derived build trace entries must be kept in addition and not instead of the underlying base build entries.
|
||||
This is the set of base build trace entries that this derived build trace is derived from.
|
||||
(The set is also a map since this miniature base build trace must be coherent, mapping each key to a single value.)
|
||||
|
||||
patternProperties:
|
||||
"^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$":
|
||||
"$ref": "store-path-v1.yaml"
|
||||
title: Dependent Store Path
|
||||
description: Store path that this dependency resolved to during the build
|
||||
additionalProperties: false
|
||||
|
||||
signatures:
|
||||
type: array
|
||||
title: Build Signatures
|
||||
description: |
|
||||
A set of cryptographic signatures attesting to the authenticity of this build trace entry.
|
||||
items:
|
||||
type: string
|
||||
title: Signature
|
||||
description: A single cryptographic signature
|
||||
|
||||
1
doc/manual/source/protocols/json/schema/derivation-options-v1
Symbolic link
1
doc/manual/source/protocols/json/schema/derivation-options-v1
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../src/libstore-tests/data/derivation
|
||||
@@ -0,0 +1,242 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/derivation-options-v1.json"
|
||||
title: Derivation Options
|
||||
description: |
|
||||
JSON representation of Nix's `DerivationOptions` type.
|
||||
|
||||
This schema describes various build-time options and constraints that can be specified for a derivation.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This JSON format is currently
|
||||
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
|
||||
> and subject to change.
|
||||
|
||||
type: object
|
||||
required:
|
||||
- outputChecks
|
||||
- unsafeDiscardReferences
|
||||
- passAsFile
|
||||
- exportReferencesGraph
|
||||
- additionalSandboxProfile
|
||||
- noChroot
|
||||
- impureHostDeps
|
||||
- impureEnvVars
|
||||
- allowLocalNetworking
|
||||
- requiredSystemFeatures
|
||||
- preferLocalBuild
|
||||
- allowSubstitutes
|
||||
properties:
|
||||
outputChecks:
|
||||
type: object
|
||||
title: Output Check
|
||||
description: |
|
||||
Constraints on what the derivation's outputs can and cannot reference.
|
||||
Can either apply to all outputs or be specified per output.
|
||||
oneOf:
|
||||
- title: Output Checks For All Outputs
|
||||
description: |
|
||||
Output checks that apply to all outputs of the derivation.
|
||||
required:
|
||||
- forAllOutputs
|
||||
properties:
|
||||
forAllOutputs:
|
||||
"$ref": "#/$defs/outputCheckSpec"
|
||||
additionalProperties: false
|
||||
|
||||
- title: Output Checks Per Output
|
||||
description: |
|
||||
Output checks specified individually for each output.
|
||||
required:
|
||||
- perOutput
|
||||
properties:
|
||||
perOutput:
|
||||
type: object
|
||||
additionalProperties:
|
||||
"$ref": "#/$defs/outputCheckSpec"
|
||||
additionalProperties: false
|
||||
|
||||
unsafeDiscardReferences:
|
||||
type: object
|
||||
title: Unsafe Discard References
|
||||
description: |
|
||||
A map specifying which references should be unsafely discarded from each output.
|
||||
This is generally not recommended and requires special permissions.
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
passAsFile:
|
||||
type: array
|
||||
title: Pass As File
|
||||
description: |
|
||||
List of environment variable names whose values should be passed as files rather than directly.
|
||||
items:
|
||||
type: string
|
||||
|
||||
exportReferencesGraph:
|
||||
type: object
|
||||
title: Export References Graph
|
||||
description: |
|
||||
Specify paths whose references graph should be exported to files.
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "deriving-path-v1.yaml"
|
||||
|
||||
additionalSandboxProfile:
|
||||
type: string
|
||||
title: Additional Sandbox Profile
|
||||
description: |
|
||||
Additional sandbox profile directives (macOS specific).
|
||||
|
||||
noChroot:
|
||||
type: boolean
|
||||
title: No Chroot
|
||||
description: |
|
||||
Whether to disable the build sandbox, if allowed.
|
||||
|
||||
impureHostDeps:
|
||||
type: array
|
||||
title: Impure Host Dependencies
|
||||
description: |
|
||||
List of host paths that the build can access.
|
||||
items:
|
||||
type: string
|
||||
|
||||
impureEnvVars:
|
||||
type: array
|
||||
title: Impure Environment Variables
|
||||
description: |
|
||||
List of environment variable names that should be passed through to the build from the calling environment.
|
||||
items:
|
||||
type: string
|
||||
|
||||
allowLocalNetworking:
|
||||
type: boolean
|
||||
title: Allow Local Networking
|
||||
description: |
|
||||
Whether the build should have access to local network (macOS specific).
|
||||
|
||||
requiredSystemFeatures:
|
||||
type: array
|
||||
title: Required System Features
|
||||
description: |
|
||||
List of system features required to build this derivation (e.g., "kvm", "nixos-test").
|
||||
items:
|
||||
type: string
|
||||
|
||||
preferLocalBuild:
|
||||
type: boolean
|
||||
title: Prefer Local Build
|
||||
description: |
|
||||
Whether this derivation should preferably be built locally rather than its outputs substituted.
|
||||
|
||||
allowSubstitutes:
|
||||
type: boolean
|
||||
title: Allow Substitutes
|
||||
description: |
|
||||
Whether substituting from other stores should be allowed for this derivation's outputs.
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
$defs:
|
||||
|
||||
outputCheckSpec:
|
||||
type: object
|
||||
title: Output Check Specification
|
||||
description: |
|
||||
Constraints on what a specific output can reference.
|
||||
required:
|
||||
- ignoreSelfRefs
|
||||
- maxSize
|
||||
- maxClosureSize
|
||||
- allowedReferences
|
||||
- allowedRequisites
|
||||
- disallowedReferences
|
||||
- disallowedRequisites
|
||||
properties:
|
||||
ignoreSelfRefs:
|
||||
type: boolean
|
||||
title: Ignore Self References
|
||||
description: |
|
||||
Whether references from this output to itself should be ignored when checking references.
|
||||
|
||||
maxSize:
|
||||
type: ["integer", "null"]
|
||||
title: Maximum Size
|
||||
description: |
|
||||
Maximum allowed size of this output in bytes, or null for no limit.
|
||||
minimum: 0
|
||||
|
||||
maxClosureSize:
|
||||
type: ["integer", "null"]
|
||||
title: Maximum Closure Size
|
||||
description: |
|
||||
Maximum allowed size of this output's closure in bytes, or null for no limit.
|
||||
minimum: 0
|
||||
|
||||
allowedReferences:
|
||||
oneOf:
|
||||
- type: array
|
||||
items:
|
||||
"$ref": "#/$defs/drvRef"
|
||||
- type: "null"
|
||||
title: Allowed References
|
||||
description: |
|
||||
If set, the output can only reference paths in this list.
|
||||
If null, no restrictions apply.
|
||||
|
||||
allowedRequisites:
|
||||
oneOf:
|
||||
- type: array
|
||||
items:
|
||||
"$ref": "#/$defs/drvRef"
|
||||
- type: "null"
|
||||
title: Allowed Requisites
|
||||
description: |
|
||||
If set, the output's closure can only contain paths in this list.
|
||||
If null, no restrictions apply.
|
||||
|
||||
disallowedReferences:
|
||||
type: array
|
||||
title: Disallowed References
|
||||
description: |
|
||||
The output must not reference any paths in this list.
|
||||
items:
|
||||
"$ref": "#/$defs/drvRef"
|
||||
|
||||
disallowedRequisites:
|
||||
type: array
|
||||
title: Disallowed Requisites
|
||||
description: |
|
||||
The output's closure must not contain any paths in this list.
|
||||
items:
|
||||
"$ref": "#/$defs/drvRef"
|
||||
additionalProperties: false
|
||||
|
||||
drvRef:
|
||||
# TODO fix bug in checker, should be `oneOf`
|
||||
anyOf:
|
||||
- type: object
|
||||
title: Current derivation Output Reference
|
||||
description: |
|
||||
A reference to a specific output of the current derivation.
|
||||
required:
|
||||
- drvPath
|
||||
- output
|
||||
properties:
|
||||
drvPath:
|
||||
type: string
|
||||
const: "self"
|
||||
title: This derivation
|
||||
description: |
|
||||
Won't be confused for a deriving path
|
||||
output:
|
||||
type: string
|
||||
title: Output Name
|
||||
description: |
|
||||
The name of the output being referenced.
|
||||
additionalProperties: false
|
||||
- "$ref": "deriving-path-v1.yaml"
|
||||
1
doc/manual/source/protocols/json/schema/file-system-object-v1
Symbolic link
1
doc/manual/source/protocols/json/schema/file-system-object-v1
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../src/libutil-tests/data/memory-source-accessor
|
||||
@@ -0,0 +1,71 @@
|
||||
"$schema": http://json-schema.org/draft-04/schema#
|
||||
"$id": https://nix.dev/manual/nix/latest/protocols/json/schema/file-system-object-v1.json
|
||||
title: File System Object
|
||||
description: |
|
||||
This schema describes the JSON representation of Nix's [File System Object](@docroot@/store/file-system-object.md).
|
||||
|
||||
The schema is recursive because file system objects contain other file system objects.
|
||||
type: object
|
||||
required: ["type"]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: ["regular", "symlink", "directory"]
|
||||
|
||||
# Enforce conditional structure based on `type`
|
||||
anyOf:
|
||||
- $ref: "#/$defs/regular"
|
||||
required: ["type", "contents"]
|
||||
|
||||
- $ref: "#/$defs/directory"
|
||||
required: ["type", "entries"]
|
||||
|
||||
- $ref: "#/$defs/symlink"
|
||||
required: ["type", "target"]
|
||||
|
||||
"$defs":
|
||||
regular:
|
||||
title: Regular File
|
||||
description: |
|
||||
See [Regular File](@docroot@/store/file-system-object.md#regular) in the manual for details.
|
||||
required: ["contents"]
|
||||
properties:
|
||||
type:
|
||||
const: "regular"
|
||||
contents:
|
||||
type: string
|
||||
description: File contents
|
||||
executable:
|
||||
type: boolean
|
||||
description: Whether the file is executable.
|
||||
default: false
|
||||
additionalProperties: false
|
||||
|
||||
directory:
|
||||
title: Directory
|
||||
description: |
|
||||
See [Directory](@docroot@/store/file-system-object.md#directory) in the manual for details.
|
||||
required: ["entries"]
|
||||
properties:
|
||||
type:
|
||||
const: "directory"
|
||||
entries:
|
||||
type: object
|
||||
description: |
|
||||
Map of names to nested file system objects (for type=directory)
|
||||
additionalProperties:
|
||||
$ref: "#"
|
||||
additionalProperties: false
|
||||
|
||||
symlink:
|
||||
title: Symbolic Link
|
||||
description: |
|
||||
See [Symbolic Link](@docroot@/store/file-system-object.md#symlink) in the manual for details.
|
||||
required: ["target"]
|
||||
properties:
|
||||
type:
|
||||
const: "symlink"
|
||||
target:
|
||||
type: string
|
||||
description: Target path of the symlink.
|
||||
additionalProperties: false
|
||||
@@ -71,7 +71,7 @@ $defs:
|
||||
Note: This field may not be present in all contexts, such as when the path is used as the key and the the store object info the value in map.
|
||||
|
||||
narHash:
|
||||
type: string
|
||||
"$ref": "./hash-v1.yaml"
|
||||
title: NAR Hash
|
||||
description: |
|
||||
Hash of the [file system object](@docroot@/store/file-system-object.md) part of the store object when serialized as a [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive).
|
||||
@@ -229,7 +229,7 @@ $defs:
|
||||
> This is an impure "`.narinfo`" field that may not be included in certain contexts.
|
||||
|
||||
downloadHash:
|
||||
type: string
|
||||
"$ref": "./hash-v1.yaml"
|
||||
title: Download Hash
|
||||
description: |
|
||||
A digest for the compressed archive itself, as opposed to the data contained within.
|
||||
|
||||
1
doc/manual/source/protocols/json/schema/store-v1
Symbolic link
1
doc/manual/source/protocols/json/schema/store-v1
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../src/libstore-tests/data/dummy-store
|
||||
90
doc/manual/source/protocols/json/schema/store-v1.yaml
Normal file
90
doc/manual/source/protocols/json/schema/store-v1.yaml
Normal file
@@ -0,0 +1,90 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-v1.json"
|
||||
title: Store
|
||||
description: |
|
||||
Experimental JSON representation of a Nix [Store](@docroot@/store/index.md).
|
||||
|
||||
This schema describes the JSON serialization of a Nix store.
|
||||
We use it for (de)serializing in-memory "dummy stores" used for testing, but in principle the data represented in this schema could live in any type of store.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This JSON format is currently
|
||||
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
|
||||
> and subject to change.
|
||||
|
||||
type: object
|
||||
required:
|
||||
- config
|
||||
- contents
|
||||
- derivations
|
||||
- buildTrace
|
||||
properties:
|
||||
config:
|
||||
"$ref": "#/$defs/storeConfig"
|
||||
|
||||
contents:
|
||||
type: object
|
||||
title: Store Objects
|
||||
description: |
|
||||
Map of [store path](@docroot@/store/store-path.md) base names to [store objects](@docroot@/store/store-object.md).
|
||||
patternProperties:
|
||||
"^[0123456789abcdfghijklmnpqrsvwxyz]{32}-.+$":
|
||||
type: object
|
||||
title: Store Object
|
||||
required:
|
||||
- info
|
||||
- contents
|
||||
properties:
|
||||
info:
|
||||
"$ref": "./store-object-info-v2.yaml#/$defs/impure"
|
||||
title: Store Object Info
|
||||
description: |
|
||||
Metadata about the [store object](@docroot@/store/store-object.md) including hash, size, references, etc.
|
||||
contents:
|
||||
"$ref": "./file-system-object-v1.yaml"
|
||||
title: File System Object Contents
|
||||
description: |
|
||||
The actual [file system object](@docroot@/store/file-system-object.md) contents of this store path.
|
||||
additionalProperties: false
|
||||
additionalProperties: false
|
||||
|
||||
derivations:
|
||||
type: object
|
||||
title: Derivations
|
||||
description: |
|
||||
Map of [store path](@docroot@/store/store-path.md) base names (always ending in `.drv`) to [derivations](@docroot@/store/derivation/index.md).
|
||||
patternProperties:
|
||||
"^[0123456789abcdfghijklmnpqrsvwxyz]{32}-.+\\.drv$":
|
||||
"$ref": "./derivation-v4.yaml"
|
||||
additionalProperties: false
|
||||
|
||||
buildTrace:
|
||||
type: object
|
||||
title: Build Trace
|
||||
description: |
|
||||
Map of output hashes (base64 SHA256) to maps of output names to realisations.
|
||||
Records which outputs have been built and their realisations.
|
||||
See [Build Trace](@docroot@/store/build-trace.md) for more details.
|
||||
patternProperties:
|
||||
"^[A-Za-z0-9+/]{43}=$":
|
||||
type: object
|
||||
additionalProperties:
|
||||
"$ref": "./build-trace-entry-v1.yaml#/$defs/value"
|
||||
additionalProperties: false
|
||||
|
||||
"$defs":
|
||||
storeConfig:
|
||||
title: Store Configuration
|
||||
description: |
|
||||
Configuration for the store, including the store directory path.
|
||||
type: object
|
||||
required:
|
||||
- store
|
||||
properties:
|
||||
store:
|
||||
type: string
|
||||
title: Store Directory
|
||||
description: |
|
||||
The store directory path (e.g., `/nix/store`).
|
||||
additionalProperties: false
|
||||
21
doc/manual/source/protocols/json/store.md
Normal file
21
doc/manual/source/protocols/json/store.md
Normal file
@@ -0,0 +1,21 @@
|
||||
{{#include store-v1-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Empty store
|
||||
|
||||
```json
|
||||
{{#include schema/store-v1/empty.json}}
|
||||
```
|
||||
|
||||
### Store with one file
|
||||
|
||||
```json
|
||||
{{#include schema/store-v1/one-flat-file.json}}
|
||||
```
|
||||
|
||||
### Store with one derivation
|
||||
|
||||
```json
|
||||
{{#include schema/store-v1/one-derivation.json}}
|
||||
```
|
||||
@@ -24,7 +24,7 @@ nar-obj-inner
|
||||
| str("type"), str("directory") directory
|
||||
;
|
||||
|
||||
regular = [ str("executable") ], str("contents"), str(contents);
|
||||
regular = [ str("executable"), str("") ], str("contents"), str(contents);
|
||||
|
||||
symlink = str("target"), str(target);
|
||||
|
||||
@@ -52,4 +52,4 @@ The Nix Archive (NAR) format is also formally described using [Kaitai Struct](ht
|
||||
{{#include nar.ksy}}
|
||||
```
|
||||
|
||||
The source of the spec can be found [here](https://github.com/nixos/nix/blob/master/src/nix-manual/source/protocols/nix-archive/nar.ksy). Contributions and improvements to the spec are welcomed.
|
||||
The source of the spec can be found [here](https://github.com/nixos/nix/blob/master/src/nix-manual/source/protocols/nix-archive/nar.ksy). Contributions and improvements to the spec are welcomed.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
We ultimately want to rectify this issue with all JSON formats to the extent allowed by our stability promises. To start with, we are changing the JSON format for derivations because the `nix derivation` commands are — in addition to being formally unstable — less widely used than other unstable commands.
|
||||
|
||||
See the documentation on the [JSON format for derivations](@docroot@/protocols/json/derivation.md) for further details.
|
||||
See the documentation on the [JSON format for derivations](@docroot@/protocols/json/derivation/index.md) for further details.
|
||||
|
||||
- C API: `nix_get_attr_name_byidx`, `nix_get_attr_byidx` take a `nix_value *` instead of `const nix_value *` [#13987](https://github.com/NixOS/nix/pull/13987)
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ There are two formats, documented separately:
|
||||
|
||||
- The legacy ["ATerm" format](@docroot@/protocols/derivation-aterm.md)
|
||||
|
||||
- The experimental, currently under development and changing [JSON format](@docroot@/protocols/json/derivation.md)
|
||||
- The experimental, currently under development and changing [JSON format](@docroot@/protocols/json/derivation/index.md)
|
||||
|
||||
Every derivation has a canonical choice of encoding used to serialize it to a store object.
|
||||
This ensures that there is a canonical [store path] used to refer to the derivation, as described in [Referencing derivations](#derivation-path).
|
||||
|
||||
@@ -3,19 +3,23 @@
|
||||
Nix uses a simplified model of the file system, which consists of file system objects.
|
||||
Every file system object is one of the following:
|
||||
|
||||
- File
|
||||
- [**Regular File**]{#regular}
|
||||
|
||||
- A possibly empty sequence of bytes for contents
|
||||
- A single boolean representing the [executable](https://en.m.wikipedia.org/wiki/File-system_permissions#Permissions) permission
|
||||
|
||||
- Directory
|
||||
- [**Directory**]{#directory}
|
||||
|
||||
Mapping of names to child file system objects
|
||||
|
||||
- [Symbolic link](https://en.m.wikipedia.org/wiki/Symbolic_link)
|
||||
- [**Symbolic link**]{#symlink}
|
||||
|
||||
An arbitrary string.
|
||||
Nix does not assign any semantics to symbolic links.
|
||||
An arbitrary string, known as the *target* of the symlink.
|
||||
|
||||
In general, Nix does not assign any semantics to symbolic links.
|
||||
Certain operations however, may make additional assumptions and attempt to use the target to find another file system object.
|
||||
|
||||
> See [the Wikpedia article on symbolic links](https://en.m.wikipedia.org/wiki/Symbolic_link) for background information if you are unfamiliar with this Unix concept.
|
||||
|
||||
File system objects and their children form a tree.
|
||||
A bare file or symlink can be a root file system object.
|
||||
|
||||
20
doc/manual/source/store/secrets.md
Normal file
20
doc/manual/source/store/secrets.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Secrets
|
||||
|
||||
The store is readable to all users on the system. For this reason, it
|
||||
is generally discouraged to allow secrets to make it into the store.
|
||||
|
||||
Even on a single-user system, separate system users isolate services
|
||||
from each other and having secrets that all local users can read
|
||||
weakens that isolation. When using external store caches the secrets
|
||||
may end up there, and on multi-user systems the secrets will be
|
||||
available to all those users.
|
||||
|
||||
Organize your derivations so that secrets are read from the filesystem
|
||||
(with appropriate access controls) at run time. Place the secrets on
|
||||
the filesystem manually or use a scheme that includes the secret in
|
||||
the store in encrypted form, and decrypts it adding the relevant
|
||||
access control on system activation.
|
||||
Several such schemes for NixOS can in the
|
||||
[comparison of secret managing schemes] on the wiki.
|
||||
|
||||
[comparison of secret managing schemes]: https://wiki.nixos.org/wiki/Comparison_of_secret_managing_schemes
|
||||
@@ -79,6 +79,8 @@
|
||||
# Not supported by nixfmt
|
||||
''^tests/functional/lang/eval-okay-deprecate-cursed-or\.nix$''
|
||||
''^tests/functional/lang/eval-okay-attrs5\.nix$''
|
||||
''^tests/functional/lang/eval-fail-dynamic-attrs-inherit\.nix$''
|
||||
''^tests/functional/lang/eval-fail-dynamic-attrs-inherit-2\.nix$''
|
||||
|
||||
# More syntax tests
|
||||
# These tests, or parts of them, should have been parse-* test cases.
|
||||
|
||||
@@ -60,4 +60,9 @@ if get_option('unit-tests')
|
||||
subproject('libflake-tests')
|
||||
endif
|
||||
subproject('nix-functional-tests')
|
||||
subproject('json-schema-checks')
|
||||
if get_option('json-schema-checks')
|
||||
subproject('json-schema-checks')
|
||||
endif
|
||||
if get_option('kaitai-struct-checks')
|
||||
subproject('kaitai-struct-checks')
|
||||
endif
|
||||
|
||||
@@ -27,3 +27,17 @@ option(
|
||||
value : false,
|
||||
description : 'Build benchmarks (requires gbenchmark)',
|
||||
)
|
||||
|
||||
option(
|
||||
'kaitai-struct-checks',
|
||||
type : 'boolean',
|
||||
value : true,
|
||||
description : 'Check the Kaitai Struct specifications (requires Kaitai Struct)',
|
||||
)
|
||||
|
||||
option(
|
||||
'json-schema-checks',
|
||||
type : 'boolean',
|
||||
value : true,
|
||||
description : 'Check JSON schema validity of schemas and examples (requires jv)',
|
||||
)
|
||||
|
||||
@@ -74,14 +74,4 @@ scope: {
|
||||
buildPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase;
|
||||
installPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase;
|
||||
});
|
||||
|
||||
libgit2 = pkgs.libgit2.overrideAttrs (attrs: {
|
||||
cmakeFlags = (attrs.cmakeFlags or [ ]) ++ [
|
||||
(lib.mesonBool "EXPERIMENTAL_SHA256" true)
|
||||
];
|
||||
|
||||
postInstall = (attrs.postInstall or "") + ''
|
||||
substituteInPlace $(find $dev/include -type f) --replace-quiet '#include "git2/' '#include "git2-experimental/'
|
||||
'';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,10 +3,118 @@
|
||||
devFlake,
|
||||
}:
|
||||
|
||||
let
|
||||
# Some helper functions
|
||||
|
||||
/**
|
||||
Compute a filtered closure of build inputs.
|
||||
|
||||
Specifically, `buildInputsClosure cond startSet` computes the closure formed
|
||||
by recursive application of `p: filter cond p.buildInputs ++ filter cond p.propagatedBuildInputs`
|
||||
to `startSet`.
|
||||
|
||||
Example:
|
||||
```nix
|
||||
builtInputsClosure isInternal [ pkg1 pkg2 ]
|
||||
=> [ pkg1 pkg3 pkg2 pkg10 ]
|
||||
```
|
||||
|
||||
Note: order tbd
|
||||
|
||||
Note: `startSet` is *NOT* filtered.
|
||||
*/
|
||||
buildInputsClosureCond =
|
||||
cond: startSet:
|
||||
let
|
||||
closure = builtins.genericClosure {
|
||||
startSet = map (d: {
|
||||
key = d.drvPath;
|
||||
value = d;
|
||||
}) startSet;
|
||||
operator =
|
||||
d:
|
||||
let
|
||||
r =
|
||||
map
|
||||
(d': {
|
||||
key = d'.drvPath;
|
||||
value = d';
|
||||
})
|
||||
(
|
||||
lib.filter cond d.value.buildInputs or [ ] ++ lib.filter cond d.value.propagatedBuildInputs or [ ]
|
||||
);
|
||||
in
|
||||
r;
|
||||
};
|
||||
in
|
||||
map (item: item.value) closure;
|
||||
|
||||
/**
|
||||
`[ pkg1 pkg2 ]` -> `{ "...-pkg2.drv" = null; "...-pkg1.drv" = null }`
|
||||
|
||||
Note: fairly arbitrary order (hash based). Use for efficient set membership test only.
|
||||
*/
|
||||
byDrvPath =
|
||||
l:
|
||||
lib.listToAttrs (
|
||||
map (c: {
|
||||
name =
|
||||
# Just a lookup key
|
||||
builtins.unsafeDiscardStringContext c.drvPath;
|
||||
value = null;
|
||||
}) l
|
||||
);
|
||||
|
||||
/**
|
||||
Stable dedup.
|
||||
|
||||
Unlike `listToAttrs` -> `attrValues`, this preserves the input ordering,
|
||||
which is more predictable ("deterministic") than e.g. sorting store paths,
|
||||
whose hashes affect the ordering on every change.
|
||||
*/
|
||||
# TODO: add to Nixpkgs lib, refer from uniqueStrings
|
||||
dedupByString =
|
||||
key: l:
|
||||
let
|
||||
r =
|
||||
lib.foldl'
|
||||
(
|
||||
a@{ list, set }:
|
||||
elem:
|
||||
let
|
||||
k = builtins.unsafeDiscardStringContext (key elem);
|
||||
in
|
||||
if set ? ${k} then
|
||||
a
|
||||
else
|
||||
let
|
||||
# Note: O(n²) copying. Use linkedLists to concat them in one go at the end.
|
||||
# https://github.com/NixOS/nixpkgs/pull/452088
|
||||
newList = [ elem ] ++ list;
|
||||
newSet = set // {
|
||||
${k} = null;
|
||||
};
|
||||
in
|
||||
builtins.seq newList builtins.seq newSet {
|
||||
list = newList;
|
||||
set = newSet;
|
||||
}
|
||||
)
|
||||
{
|
||||
list = [ ];
|
||||
set = { };
|
||||
}
|
||||
l;
|
||||
in
|
||||
r.list;
|
||||
|
||||
in
|
||||
|
||||
{ pkgs }:
|
||||
|
||||
# TODO: don't use nix-util for this?
|
||||
pkgs.nixComponents2.nix-util.overrideAttrs (
|
||||
attrs:
|
||||
finalAttrs: prevAttrs:
|
||||
|
||||
let
|
||||
stdenv = pkgs.nixDependencies2.stdenv;
|
||||
@@ -21,13 +129,93 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
|
||||
"-D${prefix}:${rest}";
|
||||
havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix;
|
||||
ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags;
|
||||
|
||||
availableComponents = lib.filterAttrs (
|
||||
k: v: lib.meta.availableOn pkgs.hostPlatform v
|
||||
) allComponents;
|
||||
|
||||
activeComponents = buildInputsClosureCond isInternal (
|
||||
lib.attrValues (finalAttrs.passthru.config.getComponents availableComponents)
|
||||
);
|
||||
|
||||
allComponents = lib.filterAttrs (k: v: lib.isDerivation v) pkgs.nixComponents2;
|
||||
internalDrvs = byDrvPath (
|
||||
# Drop the attr names (not present in buildInputs anyway)
|
||||
lib.attrValues availableComponents
|
||||
++ lib.concatMap (c: lib.attrValues c.tests or { }) (lib.attrValues availableComponents)
|
||||
);
|
||||
|
||||
isInternal =
|
||||
dep: internalDrvs ? ${builtins.unsafeDiscardStringContext dep.drvPath or "_non-existent_"};
|
||||
|
||||
in
|
||||
{
|
||||
pname = "shell-for-" + attrs.pname;
|
||||
pname = "shell-for-nix";
|
||||
|
||||
passthru = {
|
||||
inherit activeComponents;
|
||||
|
||||
# We use this attribute to store non-derivation values like functions and
|
||||
# perhaps other things that are primarily for overriding and not the shell.
|
||||
config = {
|
||||
# Default getComponents
|
||||
getComponents =
|
||||
c:
|
||||
builtins.removeAttrs c (
|
||||
lib.optionals (!havePerl) [ "nix-perl-bindings" ]
|
||||
++ lib.optionals (!buildCanExecuteHost) [ "nix-manual" ]
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
Produce a devShell for a given set of nix components
|
||||
|
||||
Example:
|
||||
|
||||
```nix
|
||||
shell.withActiveComponents (c: {
|
||||
inherit (c) nix-util;
|
||||
})
|
||||
```
|
||||
*/
|
||||
withActiveComponents =
|
||||
f2:
|
||||
finalAttrs.finalPackage.overrideAttrs (
|
||||
finalAttrs: prevAttrs: {
|
||||
passthru = prevAttrs.passthru // {
|
||||
config = prevAttrs.passthru.config // {
|
||||
getComponents = f2;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
small =
|
||||
(finalAttrs.finalPackage.withActiveComponents (
|
||||
c:
|
||||
lib.intersectAttrs (lib.genAttrs [
|
||||
"nix-cli"
|
||||
"nix-util-tests"
|
||||
"nix-store-tests"
|
||||
"nix-expr-tests"
|
||||
"nix-fetchers-tests"
|
||||
"nix-flake-tests"
|
||||
"nix-functional-tests"
|
||||
"nix-perl-bindings"
|
||||
] (_: null)) c
|
||||
)).overrideAttrs
|
||||
(o: {
|
||||
mesonFlags = o.mesonFlags ++ [
|
||||
# TODO: infer from activeComponents or vice versa
|
||||
"-Dkaitai-struct-checks=false"
|
||||
"-Djson-schema-checks=false"
|
||||
];
|
||||
});
|
||||
};
|
||||
|
||||
# Remove the version suffix to avoid unnecessary attempts to substitute in nix develop
|
||||
version = lib.fileContents ../.version;
|
||||
name = attrs.pname;
|
||||
name = finalAttrs.pname;
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
shellHook = ''
|
||||
@@ -85,6 +273,8 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
|
||||
CXX_LD = "mold";
|
||||
};
|
||||
|
||||
dontUseCmakeConfigure = true;
|
||||
|
||||
mesonFlags =
|
||||
map (transformFlag "libutil") (ignoreCrossFile pkgs.nixComponents2.nix-util.mesonFlags)
|
||||
++ map (transformFlag "libstore") (ignoreCrossFile pkgs.nixComponents2.nix-store.mesonFlags)
|
||||
@@ -98,17 +288,9 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
|
||||
nativeBuildInputs =
|
||||
let
|
||||
inputs =
|
||||
attrs.nativeBuildInputs or [ ]
|
||||
++ pkgs.nixComponents2.nix-util.nativeBuildInputs
|
||||
++ pkgs.nixComponents2.nix-store.nativeBuildInputs
|
||||
++ pkgs.nixComponents2.nix-fetchers.nativeBuildInputs
|
||||
++ pkgs.nixComponents2.nix-expr.nativeBuildInputs
|
||||
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.nativeBuildInputs
|
||||
++ lib.optionals buildCanExecuteHost pkgs.nixComponents2.nix-manual.externalNativeBuildInputs
|
||||
++ pkgs.nixComponents2.nix-internal-api-docs.nativeBuildInputs
|
||||
++ pkgs.nixComponents2.nix-external-api-docs.nativeBuildInputs
|
||||
++ pkgs.nixComponents2.nix-functional-tests.externalNativeBuildInputs
|
||||
++ pkgs.nixComponents2.nix-json-schema-checks.externalNativeBuildInputs
|
||||
dedupByString (v: "${v}") (
|
||||
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.nativeBuildInputs) activeComponents)
|
||||
)
|
||||
++ lib.optional (
|
||||
!buildCanExecuteHost
|
||||
# Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479
|
||||
@@ -117,9 +299,7 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
|
||||
&& lib.meta.availableOn stdenv.buildPlatform (stdenv.hostPlatform.emulator pkgs.buildPackages)
|
||||
) pkgs.buildPackages.mesonEmulatorHook
|
||||
++ [
|
||||
pkgs.buildPackages.cmake
|
||||
pkgs.buildPackages.gnused
|
||||
pkgs.buildPackages.changelog-d
|
||||
modular.pre-commit.settings.package
|
||||
(pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript)
|
||||
pkgs.buildPackages.nixfmt-rfc-style
|
||||
@@ -136,18 +316,22 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
|
||||
# from making its way into NIX_CFLAGS_COMPILE.
|
||||
lib.filter (p: !lib.hasInfix "separate-debug-info" p) inputs;
|
||||
|
||||
propagatedNativeBuildInputs = dedupByString (v: "${v}") (
|
||||
lib.filter (x: !isInternal x) (
|
||||
lib.lists.concatMap (c: c.propagatedNativeBuildInputs) activeComponents
|
||||
)
|
||||
);
|
||||
|
||||
buildInputs = [
|
||||
pkgs.gbenchmark
|
||||
]
|
||||
++ attrs.buildInputs or [ ]
|
||||
++ pkgs.nixComponents2.nix-util.buildInputs
|
||||
++ pkgs.nixComponents2.nix-store.buildInputs
|
||||
++ pkgs.nixComponents2.nix-store-tests.externalBuildInputs
|
||||
++ pkgs.nixComponents2.nix-fetchers.buildInputs
|
||||
++ pkgs.nixComponents2.nix-expr.buildInputs
|
||||
++ pkgs.nixComponents2.nix-expr.externalPropagatedBuildInputs
|
||||
++ pkgs.nixComponents2.nix-cmd.buildInputs
|
||||
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.externalBuildInputs
|
||||
++ dedupByString (v: "${v}") (
|
||||
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.buildInputs) activeComponents)
|
||||
)
|
||||
++ lib.optional havePerl pkgs.perl;
|
||||
|
||||
propagatedBuildInputs = dedupByString (v: "${v}") (
|
||||
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.propagatedBuildInputs) activeComponents)
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
1
src/json-schema-checks/derivation-options
Symbolic link
1
src/json-schema-checks/derivation-options
Symbolic link
@@ -0,0 +1 @@
|
||||
../libstore-tests/data/derivation
|
||||
1
src/json-schema-checks/file-system-object
Symbolic link
1
src/json-schema-checks/file-system-object
Symbolic link
@@ -0,0 +1 @@
|
||||
../../src/libutil-tests/data/memory-source-accessor
|
||||
@@ -20,6 +20,14 @@ schema_dir = meson.current_source_dir() / 'schema'
|
||||
|
||||
# Get all example files
|
||||
schemas = [
|
||||
{
|
||||
'stem' : 'file-system-object',
|
||||
'schema' : schema_dir / 'file-system-object-v1.yaml',
|
||||
'files' : [
|
||||
'simple.json',
|
||||
'complex.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'hash',
|
||||
'schema' : schema_dir / 'hash-v1.yaml',
|
||||
@@ -63,6 +71,18 @@ schemas = [
|
||||
'with-signature.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'derivation-options',
|
||||
'schema' : schema_dir / 'derivation-options-v1.yaml',
|
||||
'files' : [
|
||||
'ia' / 'defaults.json',
|
||||
'ia' / 'all_set.json',
|
||||
'ia' / 'structuredAttrs_defaults.json',
|
||||
'ia' / 'structuredAttrs_all_set.json',
|
||||
'ca' / 'all_set.json',
|
||||
'ca' / 'structuredAttrs_all_set.json',
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
# Derivation and Derivation output
|
||||
@@ -192,6 +212,19 @@ schemas += [
|
||||
},
|
||||
]
|
||||
|
||||
# Dummy store
|
||||
schemas += [
|
||||
{
|
||||
'stem' : 'store',
|
||||
'schema' : schema_dir / 'store-v1.yaml',
|
||||
'files' : [
|
||||
'empty.json',
|
||||
'one-flat-file.json',
|
||||
'one-derivation.json',
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
# Validate each example against the schema
|
||||
foreach schema : schemas
|
||||
stem = schema['stem']
|
||||
|
||||
@@ -20,6 +20,7 @@ mkMesonDerivation (finalAttrs: {
|
||||
fileset = lib.fileset.unions [
|
||||
../../.version
|
||||
../../doc/manual/source/protocols/json/schema
|
||||
../../src/libutil-tests/data/memory-source-accessor
|
||||
../../src/libutil-tests/data/hash
|
||||
../../src/libstore-tests/data/content-address
|
||||
../../src/libstore-tests/data/store-path
|
||||
@@ -29,20 +30,17 @@ mkMesonDerivation (finalAttrs: {
|
||||
../../src/libstore-tests/data/path-info
|
||||
../../src/libstore-tests/data/nar-info
|
||||
../../src/libstore-tests/data/build-result
|
||||
../../src/libstore-tests/data/dummy-store
|
||||
./.
|
||||
];
|
||||
|
||||
outputs = [ "out" ];
|
||||
|
||||
passthru.externalNativeBuildInputs = [
|
||||
jsonschema
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
meson
|
||||
ninja
|
||||
]
|
||||
++ finalAttrs.passthru.externalNativeBuildInputs;
|
||||
jsonschema
|
||||
];
|
||||
|
||||
doCheck = true;
|
||||
|
||||
|
||||
1
src/json-schema-checks/store
Symbolic link
1
src/json-schema-checks/store
Symbolic link
@@ -0,0 +1 @@
|
||||
../../src/libstore-tests/data/dummy-store
|
||||
@@ -37,7 +37,15 @@ mkMesonDerivation (finalAttrs: {
|
||||
|
||||
outputs = [ "out" ];
|
||||
|
||||
passthru.externalNativeBuildInputs = [
|
||||
buildInputs = [
|
||||
gtest
|
||||
kaitai-struct-cpp-stl-runtime
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
meson
|
||||
ninja
|
||||
pkg-config
|
||||
# This can go away when we bump up to 25.11
|
||||
(kaitai-struct-compiler.overrideAttrs (finalAttrs: {
|
||||
version = "0.11";
|
||||
@@ -48,20 +56,6 @@ mkMesonDerivation (finalAttrs: {
|
||||
}))
|
||||
];
|
||||
|
||||
passthru.externalBuildInputs = [
|
||||
gtest
|
||||
kaitai-struct-cpp-stl-runtime
|
||||
];
|
||||
|
||||
buildInputs = finalAttrs.passthru.externalBuildInputs;
|
||||
|
||||
nativeBuildInputs = [
|
||||
meson
|
||||
ninja
|
||||
pkg-config
|
||||
]
|
||||
++ finalAttrs.passthru.externalNativeBuildInputs;
|
||||
|
||||
doCheck = true;
|
||||
|
||||
mesonCheckFlags = [ "--print-errorlogs" ];
|
||||
@@ -71,6 +65,6 @@ mkMesonDerivation (finalAttrs: {
|
||||
'';
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.all;
|
||||
platforms = lib.platforms.unix;
|
||||
};
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "nix/cmd/command.hh"
|
||||
#include "nix/cmd/legacy.hh"
|
||||
#include "nix/cmd/markdown.hh"
|
||||
#include "nix/store/store-open.hh"
|
||||
#include "nix/store/local-fs-store.hh"
|
||||
@@ -14,6 +15,18 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
RegisterCommand::Commands & RegisterCommand::commands()
|
||||
{
|
||||
static RegisterCommand::Commands commands;
|
||||
return commands;
|
||||
}
|
||||
|
||||
RegisterLegacyCommand::Commands & RegisterLegacyCommand::commands()
|
||||
{
|
||||
static RegisterLegacyCommand::Commands commands;
|
||||
return commands;
|
||||
}
|
||||
|
||||
nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & prefix)
|
||||
{
|
||||
nix::Commands res;
|
||||
@@ -284,7 +297,7 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
|
||||
|
||||
MixDefaultProfile::MixDefaultProfile()
|
||||
{
|
||||
profile = getDefaultProfile();
|
||||
profile = getDefaultProfile().string();
|
||||
}
|
||||
|
||||
MixEnvironment::MixEnvironment()
|
||||
@@ -378,7 +391,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
|
||||
auto symlink = outLink;
|
||||
if (i)
|
||||
symlink += fmt("-%d", i);
|
||||
store.addPermRoot(bo.path, absPath(symlink.string()));
|
||||
store.addPermRoot(bo.path, absPath(symlink).string());
|
||||
},
|
||||
[&](const BuiltPath::Built & bfd) {
|
||||
for (auto & output : bfd.outputs) {
|
||||
@@ -387,7 +400,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
|
||||
symlink += fmt("-%d", i);
|
||||
if (output.first != "out")
|
||||
symlink += fmt("-%s", output.first);
|
||||
store.addPermRoot(output.second, absPath(symlink.string()));
|
||||
store.addPermRoot(output.second, absPath(symlink).string());
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -33,7 +33,8 @@ EvalSettings evalSettings{
|
||||
// FIXME `parseFlakeRef` should take a `std::string_view`.
|
||||
auto flakeRef = parseFlakeRef(fetchSettings, std::string{rest}, {}, true, false);
|
||||
debug("fetching flake search path element '%s''", rest);
|
||||
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
|
||||
auto [accessor, lockedRef] =
|
||||
flakeRef.resolve(fetchSettings, *state.store).lazyFetch(fetchSettings, *state.store);
|
||||
auto storePath = nix::fetchToStore(
|
||||
state.fetchSettings,
|
||||
*state.store,
|
||||
@@ -164,22 +165,22 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
||||
state.parseExprFromString(
|
||||
arg.expr,
|
||||
compatibilitySettings.nixShellShebangArgumentsRelativeToScript
|
||||
? state.rootPath(absPath(getCommandBaseDir()))
|
||||
? state.rootPath(absPath(getCommandBaseDir()).string())
|
||||
: state.rootPath(".")));
|
||||
},
|
||||
[&](const AutoArgString & arg) { v->mkString(arg.s); },
|
||||
[&](const AutoArgFile & arg) { v->mkString(readFile(arg.path.string())); },
|
||||
[&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO)); }},
|
||||
[&](const AutoArgString & arg) { v->mkString(arg.s, state.mem); },
|
||||
[&](const AutoArgFile & arg) { v->mkString(readFile(arg.path.string()), state.mem); },
|
||||
[&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO), state.mem); }},
|
||||
arg);
|
||||
res.insert(state.symbols.create(name), v);
|
||||
}
|
||||
return res.finish();
|
||||
}
|
||||
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir)
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s, const std::filesystem::path * baseDir)
|
||||
{
|
||||
if (EvalSettings::isPseudoUrl(s)) {
|
||||
auto accessor = fetchers::downloadTarball(state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s));
|
||||
auto accessor = fetchers::downloadTarball(*state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s));
|
||||
auto storePath = fetchToStore(state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy);
|
||||
return state.storePath(storePath);
|
||||
}
|
||||
@@ -187,7 +188,8 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
|
||||
else if (hasPrefix(s, "flake:")) {
|
||||
experimentalFeatureSettings.require(Xp::Flakes);
|
||||
auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false);
|
||||
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
|
||||
auto [accessor, lockedRef] =
|
||||
flakeRef.resolve(fetchSettings, *state.store).lazyFetch(fetchSettings, *state.store);
|
||||
auto storePath = nix::fetchToStore(
|
||||
state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
|
||||
state.allowPath(storePath);
|
||||
@@ -195,12 +197,13 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
|
||||
}
|
||||
|
||||
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
|
||||
Path p(s.substr(1, s.size() - 2));
|
||||
// Should perhaps be a `CanonPath`?
|
||||
std::string p(s.substr(1, s.size() - 2));
|
||||
return state.findFile(p);
|
||||
}
|
||||
|
||||
else
|
||||
return state.rootPath(baseDir ? absPath(s, *baseDir) : absPath(s));
|
||||
return state.rootPath(absPath(std::filesystem::path{s}, baseDir).string());
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -134,7 +134,7 @@ struct MixFlakeOptions : virtual Args, EvalCommand
|
||||
|
||||
struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||
{
|
||||
std::optional<Path> file;
|
||||
std::optional<std::filesystem::path> file;
|
||||
std::optional<std::string> expr;
|
||||
|
||||
SourceExprCommand();
|
||||
@@ -286,11 +286,7 @@ struct RegisterCommand
|
||||
{
|
||||
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
|
||||
|
||||
static Commands & commands()
|
||||
{
|
||||
static Commands commands;
|
||||
return commands;
|
||||
}
|
||||
static Commands & commands();
|
||||
|
||||
RegisterCommand(std::vector<std::string> && name, std::function<ref<Command>()> command)
|
||||
{
|
||||
@@ -314,7 +310,7 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
|
||||
|
||||
struct MixProfile : virtual StoreCommand
|
||||
{
|
||||
std::optional<Path> profile;
|
||||
std::optional<std::filesystem::path> profile;
|
||||
|
||||
MixProfile();
|
||||
|
||||
|
||||
@@ -84,6 +84,6 @@ private:
|
||||
/**
|
||||
* @param baseDir Optional [base directory](https://nix.dev/manual/nix/development/glossary#gloss-base-directory)
|
||||
*/
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir = nullptr);
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s, const std::filesystem::path * baseDir = nullptr);
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -17,7 +17,7 @@ class AttrCursor;
|
||||
struct App
|
||||
{
|
||||
std::vector<DerivedPath> context;
|
||||
Path program;
|
||||
std::filesystem::path program;
|
||||
// FIXME: add args, sandbox settings, metadata, ...
|
||||
};
|
||||
|
||||
|
||||
@@ -13,11 +13,7 @@ struct RegisterLegacyCommand
|
||||
{
|
||||
typedef std::map<std::string, MainFunction> Commands;
|
||||
|
||||
static Commands & commands()
|
||||
{
|
||||
static Commands commands;
|
||||
return commands;
|
||||
}
|
||||
static Commands & commands();
|
||||
|
||||
RegisterLegacyCommand(const std::string & name, MainFunction fun)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,16 @@ struct AbstractNixRepl
|
||||
|
||||
typedef std::vector<std::pair<Value *, std::string>> AnnotatedValues;
|
||||
|
||||
using RunNix = void(Path program, const Strings & args, const std::optional<std::string> & input);
|
||||
/**
|
||||
* Run a nix executable
|
||||
*
|
||||
* @todo this is a layer violation
|
||||
*
|
||||
* @param programName Name of the command, e.g. `nix` or `nix-env`.
|
||||
* @param args aguments to the command.
|
||||
*/
|
||||
using RunNix =
|
||||
void(const std::string & programName, const Strings & args, const std::optional<std::string> & input);
|
||||
|
||||
/**
|
||||
* @param runNix Function to run the nix CLI to support various
|
||||
|
||||
@@ -132,7 +132,7 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
lockFlags.writeLockFile = false;
|
||||
lockFlags.inputOverrides.insert_or_assign(
|
||||
flake::parseInputAttrPath(inputAttrPath),
|
||||
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()), true));
|
||||
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()).string(), true));
|
||||
}},
|
||||
.completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) {
|
||||
if (n == 0) {
|
||||
@@ -173,7 +173,7 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
auto flake = flake::lockFlake(
|
||||
flakeSettings,
|
||||
*evalState,
|
||||
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir())),
|
||||
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()).string()),
|
||||
{.writeLockFile = false});
|
||||
for (auto & [inputName, input] : flake.lockFile.root->inputs) {
|
||||
auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes
|
||||
@@ -263,7 +263,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
|
||||
|
||||
evalSettings.pureEval = false;
|
||||
auto state = getEvalState();
|
||||
auto e = state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, *file)));
|
||||
auto e = state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, file->string())));
|
||||
|
||||
Value root;
|
||||
state->eval(e, root);
|
||||
@@ -409,7 +409,7 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
|
||||
Args::completeDir(completions, 0, prefix);
|
||||
|
||||
/* Look for registry entries that match the prefix. */
|
||||
for (auto & registry : fetchers::getRegistries(fetchSettings, store)) {
|
||||
for (auto & registry : fetchers::getRegistries(fetchSettings, *store)) {
|
||||
for (auto & entry : registry->entries) {
|
||||
auto from = entry.from.to_string();
|
||||
if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
|
||||
@@ -465,10 +465,10 @@ Installables SourceExprCommand::parseInstallables(ref<Store> store, std::vector<
|
||||
state->eval(e, *vFile);
|
||||
} else if (file) {
|
||||
auto dir = absPath(getCommandBaseDir());
|
||||
state->evalFile(lookupFileArg(*state, *file, &dir), *vFile);
|
||||
state->evalFile(lookupFileArg(*state, file->string(), &dir), *vFile);
|
||||
} else {
|
||||
Path dir = absPath(getCommandBaseDir());
|
||||
auto e = state->parseExprFromString(*expr, state->rootPath(dir));
|
||||
auto dir = absPath(getCommandBaseDir());
|
||||
auto e = state->parseExprFromString(*expr, state->rootPath(dir.string()));
|
||||
state->eval(e, *vFile);
|
||||
}
|
||||
|
||||
@@ -801,7 +801,8 @@ std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
|
||||
std::vector<FlakeRef> res;
|
||||
res.reserve(rawInstallables.size());
|
||||
for (const auto & i : rawInstallables)
|
||||
res.push_back(parseFlakeRefWithFragment(fetchSettings, expandTilde(i), absPath(getCommandBaseDir())).first);
|
||||
res.push_back(
|
||||
parseFlakeRefWithFragment(fetchSettings, expandTilde(i), absPath(getCommandBaseDir()).string()).first);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -820,7 +821,8 @@ void RawInstallablesCommand::run(ref<Store> store)
|
||||
|
||||
std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
|
||||
{
|
||||
return {parseFlakeRefWithFragment(fetchSettings, expandTilde(_installable), absPath(getCommandBaseDir())).first};
|
||||
return {parseFlakeRefWithFragment(fetchSettings, expandTilde(_installable), absPath(getCommandBaseDir()).string())
|
||||
.first};
|
||||
}
|
||||
|
||||
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)
|
||||
|
||||
@@ -58,8 +58,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
|
||||
{
|
||||
size_t debugTraceIndex;
|
||||
|
||||
// Arguments passed to :load, saved so they can be reloaded with :reload
|
||||
Strings loadedFiles;
|
||||
std::list<std::filesystem::path> loadedFiles;
|
||||
// Arguments passed to :load-flake, saved so they can be reloaded with :reload
|
||||
Strings loadedFlakes;
|
||||
std::function<AnnotatedValues()> getValues;
|
||||
@@ -73,7 +72,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
|
||||
|
||||
RunNix * runNixPtr;
|
||||
|
||||
void runNix(Path program, const Strings & args, const std::optional<std::string> & input = {});
|
||||
void runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input = {});
|
||||
|
||||
std::unique_ptr<ReplInteracter> interacter;
|
||||
|
||||
@@ -92,7 +91,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
|
||||
StorePath getDerivationPath(Value & v);
|
||||
ProcessLineResult processLine(std::string line);
|
||||
|
||||
void loadFile(const Path & path);
|
||||
void loadFile(const std::filesystem::path & path);
|
||||
void loadFlake(const std::string & flakeRef);
|
||||
void loadFiles();
|
||||
void loadFlakes();
|
||||
@@ -539,7 +538,9 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
Value v;
|
||||
evalString(arg, v);
|
||||
StorePath drvPath = getDerivationPath(v);
|
||||
Path drvPathRaw = state->store->printStorePath(drvPath);
|
||||
// N.B. This need not be a local / native file path. For
|
||||
// example, we might be using an SSH store to a different OS.
|
||||
std::string drvPathRaw = state->store->printStorePath(drvPath);
|
||||
|
||||
if (command == ":b" || command == ":bl") {
|
||||
state->store->buildPaths({
|
||||
@@ -712,12 +713,12 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
return ProcessLineResult::PromptAgain;
|
||||
}
|
||||
|
||||
void NixRepl::loadFile(const Path & path)
|
||||
void NixRepl::loadFile(const std::filesystem::path & path)
|
||||
{
|
||||
loadedFiles.remove(path);
|
||||
loadedFiles.push_back(path);
|
||||
Value v, v2;
|
||||
state->evalFile(lookupFileArg(*state, path), v);
|
||||
state->evalFile(lookupFileArg(*state, path.string()), v);
|
||||
state->autoCallFunction(*autoArgs, v, v2);
|
||||
addAttrsToScope(v2);
|
||||
}
|
||||
@@ -738,8 +739,8 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
|
||||
}
|
||||
|
||||
auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true);
|
||||
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
||||
throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS);
|
||||
if (evalSettings.pureEval && !flakeRef.input.isLocked(fetchSettings))
|
||||
throw Error("cannot use ':load-flake' on unlocked flake reference '%s' (use --impure to override)", flakeRefS);
|
||||
|
||||
Value v;
|
||||
|
||||
@@ -790,7 +791,7 @@ void NixRepl::reloadFilesAndFlakes()
|
||||
|
||||
void NixRepl::loadFiles()
|
||||
{
|
||||
Strings old = loadedFiles;
|
||||
decltype(loadedFiles) old = loadedFiles;
|
||||
loadedFiles.clear();
|
||||
|
||||
for (auto & i : old) {
|
||||
@@ -888,7 +889,7 @@ void NixRepl::evalString(std::string s, Value & v)
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
}
|
||||
|
||||
void NixRepl::runNix(Path program, const Strings & args, const std::optional<std::string> & input)
|
||||
void NixRepl::runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input)
|
||||
{
|
||||
if (runNixPtr)
|
||||
(*runNixPtr)(program, args, input);
|
||||
|
||||
@@ -69,8 +69,8 @@ nix_err nix_expr_eval_from_string(
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path)));
|
||||
state->state.eval(parsedExpr, value->value);
|
||||
state->state.forceValue(value->value, nix::noPos);
|
||||
state->state.eval(parsedExpr, *value->value);
|
||||
state->state.forceValue(*value->value, nix::noPos);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
@@ -80,8 +80,8 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, n
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
state->state.callFunction(fn->value, arg->value, value->value, nix::noPos);
|
||||
state->state.forceValue(value->value, nix::noPos);
|
||||
state->state.callFunction(*fn->value, *arg->value, *value->value, nix::noPos);
|
||||
state->state.forceValue(*value->value, nix::noPos);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
@@ -91,9 +91,15 @@ nix_err nix_value_call_multi(
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
|
||||
std::vector<nix::Value *> internal_args;
|
||||
internal_args.reserve(nargs);
|
||||
for (size_t i = 0; i < nargs; i++)
|
||||
internal_args.push_back(args[i]->value);
|
||||
|
||||
try {
|
||||
state->state.callFunction(fn->value, {(nix::Value **) args, nargs}, value->value, nix::noPos);
|
||||
state->state.forceValue(value->value, nix::noPos);
|
||||
state->state.callFunction(*fn->value, {internal_args.data(), nargs}, *value->value, nix::noPos);
|
||||
state->state.forceValue(*value->value, nix::noPos);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
@@ -103,7 +109,7 @@ nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value *
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
state->state.forceValue(value->value, nix::noPos);
|
||||
state->state.forceValue(*value->value, nix::noPos);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
@@ -113,7 +119,7 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_val
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
state->state.forceValueDeep(value->value);
|
||||
state->state.forceValueDeep(*value->value);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
@@ -137,6 +143,8 @@ nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Sto
|
||||
|
||||
void nix_eval_state_builder_free(nix_eval_state_builder * builder)
|
||||
{
|
||||
if (builder)
|
||||
builder->~nix_eval_state_builder();
|
||||
operator delete(builder, static_cast<std::align_val_t>(alignof(nix_eval_state_builder)));
|
||||
}
|
||||
|
||||
@@ -203,6 +211,8 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c
|
||||
|
||||
void nix_state_free(EvalState * state)
|
||||
{
|
||||
if (state)
|
||||
state->~EvalState();
|
||||
operator delete(state, static_cast<std::align_val_t>(alignof(EvalState)));
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,13 @@ struct ListBuilder
|
||||
|
||||
struct nix_value
|
||||
{
|
||||
nix::Value value;
|
||||
nix::Value * value;
|
||||
/**
|
||||
* As we move to a managed heap, we need EvalMemory in more places. Ideally, we would take in EvalState or
|
||||
* EvalMemory as an argument when we need it, but we don't want to make changes to the stable C api, so we stuff it
|
||||
* into the nix_value that will get passed in to the relevant functions.
|
||||
*/
|
||||
nix::EvalMemory * mem;
|
||||
};
|
||||
|
||||
struct nix_string_return
|
||||
|
||||
@@ -20,7 +20,7 @@ static const nix::Value & check_value_not_null(const nix_value * value)
|
||||
if (!value) {
|
||||
throw std::runtime_error("nix_value is null");
|
||||
}
|
||||
return *((const nix::Value *) value);
|
||||
return *value->value;
|
||||
}
|
||||
|
||||
static nix::Value & check_value_not_null(nix_value * value)
|
||||
@@ -28,7 +28,7 @@ static nix::Value & check_value_not_null(nix_value * value)
|
||||
if (!value) {
|
||||
throw std::runtime_error("nix_value is null");
|
||||
}
|
||||
return value->value;
|
||||
return *value->value;
|
||||
}
|
||||
|
||||
static const nix::Value & check_value_in(const nix_value * value)
|
||||
@@ -58,9 +58,14 @@ static nix::Value & check_value_out(nix_value * value)
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline nix_value * as_nix_value_ptr(nix::Value * v)
|
||||
static inline nix_value * new_nix_value(nix::Value * v, nix::EvalMemory & mem)
|
||||
{
|
||||
return reinterpret_cast<nix_value *>(v);
|
||||
nix_value * ret = new (mem.allocBytes(sizeof(nix_value))) nix_value{
|
||||
.value = v,
|
||||
.mem = &mem,
|
||||
};
|
||||
nix_gc_incref(nullptr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +74,13 @@ static inline nix_value * as_nix_value_ptr(nix::Value * v)
|
||||
* Deals with errors and converts arguments from C++ into C types.
|
||||
*/
|
||||
static void nix_c_primop_wrapper(
|
||||
PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v)
|
||||
PrimOpFun f,
|
||||
void * userdata,
|
||||
int arity,
|
||||
nix::EvalState & state,
|
||||
const nix::PosIdx pos,
|
||||
nix::Value ** args,
|
||||
nix::Value & v)
|
||||
{
|
||||
nix_c_context ctx;
|
||||
|
||||
@@ -85,8 +96,15 @@ static void nix_c_primop_wrapper(
|
||||
// ok because we don't see a need for this yet (e.g. inspecting thunks,
|
||||
// or maybe something to make blackholes work better; we don't know).
|
||||
nix::Value vTmp;
|
||||
nix_value * vTmpPtr = new_nix_value(&vTmp, state.mem);
|
||||
|
||||
f(userdata, &ctx, (EvalState *) &state, (nix_value **) args, (nix_value *) &vTmp);
|
||||
std::vector<nix_value *> external_args;
|
||||
external_args.reserve(arity);
|
||||
for (int i = 0; i < arity; i++) {
|
||||
nix_value * external_arg = new_nix_value(args[i], state.mem);
|
||||
external_args.push_back(external_arg);
|
||||
}
|
||||
f(userdata, &ctx, (EvalState *) &state, external_args.data(), vTmpPtr);
|
||||
|
||||
if (ctx.last_err_code != NIX_OK) {
|
||||
/* TODO: Throw different errors depending on the error code */
|
||||
@@ -135,7 +153,7 @@ PrimOp * nix_alloc_primop(
|
||||
.args = {},
|
||||
.arity = (size_t) arity,
|
||||
.doc = doc,
|
||||
.fun = std::bind(nix_c_primop_wrapper, fun, user_data, _1, _2, _3, _4)};
|
||||
.fun = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)};
|
||||
if (args)
|
||||
for (size_t i = 0; args[i]; i++)
|
||||
p->args.emplace_back(*args);
|
||||
@@ -160,8 +178,7 @@ nix_value * nix_alloc_value(nix_c_context * context, EvalState * state)
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
nix_value * res = as_nix_value_ptr(state->state.allocValue());
|
||||
nix_gc_incref(nullptr, res);
|
||||
nix_value * res = new_nix_value(state->state.allocValue(), state->state.mem);
|
||||
return res;
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
@@ -331,10 +348,10 @@ nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value,
|
||||
return nullptr;
|
||||
}
|
||||
auto * p = v.listView()[ix];
|
||||
nix_gc_incref(nullptr, p);
|
||||
if (p != nullptr)
|
||||
state->state.forceValue(*p, nix::noPos);
|
||||
return as_nix_value_ptr(p);
|
||||
if (p == nullptr)
|
||||
return nullptr;
|
||||
state->state.forceValue(*p, nix::noPos);
|
||||
return new_nix_value(p, state->state.mem);
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
}
|
||||
@@ -352,9 +369,8 @@ nix_get_list_byidx_lazy(nix_c_context * context, const nix_value * value, EvalSt
|
||||
return nullptr;
|
||||
}
|
||||
auto * p = v.listView()[ix];
|
||||
nix_gc_incref(nullptr, p);
|
||||
// Note: intentionally NOT calling forceValue() to keep the element lazy
|
||||
return as_nix_value_ptr(p);
|
||||
return new_nix_value(p, state->state.mem);
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
}
|
||||
@@ -369,9 +385,8 @@ nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value
|
||||
nix::Symbol s = state->state.symbols.create(name);
|
||||
auto attr = v.attrs()->get(s);
|
||||
if (attr) {
|
||||
nix_gc_incref(nullptr, attr->value);
|
||||
state->state.forceValue(*attr->value, nix::noPos);
|
||||
return as_nix_value_ptr(attr->value);
|
||||
return new_nix_value(attr->value, state->state.mem);
|
||||
}
|
||||
nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute");
|
||||
return nullptr;
|
||||
@@ -390,9 +405,8 @@ nix_get_attr_byname_lazy(nix_c_context * context, const nix_value * value, EvalS
|
||||
nix::Symbol s = state->state.symbols.create(name);
|
||||
auto attr = v.attrs()->get(s);
|
||||
if (attr) {
|
||||
nix_gc_incref(nullptr, attr->value);
|
||||
// Note: intentionally NOT calling forceValue() to keep the attribute lazy
|
||||
return as_nix_value_ptr(attr->value);
|
||||
return new_nix_value(attr->value, state->state.mem);
|
||||
}
|
||||
nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute");
|
||||
return nullptr;
|
||||
@@ -440,9 +454,8 @@ nix_get_attr_byidx(nix_c_context * context, nix_value * value, EvalState * state
|
||||
}
|
||||
const nix::Attr & a = (*v.attrs())[i];
|
||||
*name = state->state.symbols[a.name].c_str();
|
||||
nix_gc_incref(nullptr, a.value);
|
||||
state->state.forceValue(*a.value, nix::noPos);
|
||||
return as_nix_value_ptr(a.value);
|
||||
return new_nix_value(a.value, state->state.mem);
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
}
|
||||
@@ -461,9 +474,8 @@ nix_value * nix_get_attr_byidx_lazy(
|
||||
}
|
||||
const nix::Attr & a = (*v.attrs())[i];
|
||||
*name = state->state.symbols[a.name].c_str();
|
||||
nix_gc_incref(nullptr, a.value);
|
||||
// Note: intentionally NOT calling forceValue() to keep the attribute lazy
|
||||
return as_nix_value_ptr(a.value);
|
||||
return new_nix_value(a.value, state->state.mem);
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
}
|
||||
@@ -503,7 +515,7 @@ nix_err nix_init_string(nix_c_context * context, nix_value * value, const char *
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_out(value);
|
||||
v.mkString(std::string_view(str));
|
||||
v.mkString(std::string_view(str), *value->mem);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
@@ -514,7 +526,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, nix_value *
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_out(value);
|
||||
v.mkPath(s->state.rootPath(nix::CanonPath(str)));
|
||||
v.mkPath(s->state.rootPath(nix::CanonPath(str)), s->state.mem);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ MATCHER(IsAttrs, "")
|
||||
MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s))
|
||||
{
|
||||
if (arg.type() != nString) {
|
||||
*result_listener << "Expected a string got " << arg.type();
|
||||
return false;
|
||||
}
|
||||
return arg.string_view() == s;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "nix/expr/tests/libexpr.hh"
|
||||
#include "nix/expr/value-to-json.hh"
|
||||
#include "nix/expr/static-string-data.hh"
|
||||
|
||||
namespace nix {
|
||||
// Testing the conversion to JSON
|
||||
@@ -54,7 +55,7 @@ TEST_F(JSONValueTest, IntNegative)
|
||||
TEST_F(JSONValueTest, String)
|
||||
{
|
||||
Value v;
|
||||
v.mkStringNoCopy("test");
|
||||
v.mkStringNoCopy("test"_sds);
|
||||
ASSERT_EQ(getJSONValue(v), "\"test\"");
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ TEST_F(JSONValueTest, StringQuotes)
|
||||
{
|
||||
Value v;
|
||||
|
||||
v.mkStringNoCopy("test\"");
|
||||
v.mkStringNoCopy("test\""_sds);
|
||||
ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ TEST_F(JSONValueTest, StringQuotes)
|
||||
TEST_F(JSONValueTest, DISABLED_Path)
|
||||
{
|
||||
Value v;
|
||||
v.mkPath(state.rootPath(CanonPath("/test")));
|
||||
v.mkPath(state.rootPath(CanonPath("/test")), state.mem);
|
||||
ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\"");
|
||||
}
|
||||
} /* namespace nix */
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace nixC {
|
||||
|
||||
TEST_F(nix_api_store_test, nix_eval_state_lookup_path)
|
||||
TEST_F(nix_api_expr_test, nix_eval_state_lookup_path)
|
||||
{
|
||||
auto tmpDir = nix::createTempDir();
|
||||
auto delTmpDir = std::make_unique<nix::AutoDelete>(tmpDir, true);
|
||||
@@ -42,12 +42,16 @@ TEST_F(nix_api_store_test, nix_eval_state_lookup_path)
|
||||
nix_expr_eval_from_string(ctx, state, "builtins.seq <nixos-config> <nixpkgs>", ".", value);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_state_free(state);
|
||||
|
||||
ASSERT_EQ(nix_get_type(ctx, value), NIX_TYPE_PATH);
|
||||
assert_ctx_ok();
|
||||
|
||||
auto pathStr = nix_get_path_string(ctx, value);
|
||||
assert_ctx_ok();
|
||||
ASSERT_EQ(0, strcmp(pathStr, nixpkgs.c_str()));
|
||||
|
||||
nix_gc_decref(nullptr, value);
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_expr_eval_from_string)
|
||||
|
||||
@@ -661,8 +661,14 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
CASE(R"(null)", ""),
|
||||
CASE(R"({ v = "bar"; __toString = self: self.v; })", "bar"),
|
||||
CASE(R"({ v = "bar"; __toString = self: self.v; outPath = "foo"; })", "bar"),
|
||||
CASE(R"({ outPath = "foo"; })", "foo"),
|
||||
CASE(R"(./test)", "/test")));
|
||||
CASE(R"({ outPath = "foo"; })", "foo")
|
||||
// this is broken on cygwin because canonPath("//./test", false) returns //./test
|
||||
// FIXME: don't use canonPath
|
||||
#ifndef __CYGWIN__
|
||||
,
|
||||
CASE(R"(./test)", "/test")
|
||||
#endif
|
||||
));
|
||||
#undef CASE
|
||||
|
||||
TEST_F(PrimOpTest, substring)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "nix/expr/tests/libexpr.hh"
|
||||
#include "nix/expr/static-string-data.hh"
|
||||
|
||||
#include "nix/expr/value.hh"
|
||||
#include "nix/expr/print.hh"
|
||||
@@ -35,14 +36,14 @@ TEST_F(ValuePrintingTests, tBool)
|
||||
TEST_F(ValuePrintingTests, tString)
|
||||
{
|
||||
Value vString;
|
||||
vString.mkStringNoCopy("some-string");
|
||||
vString.mkStringNoCopy("some-string"_sds);
|
||||
test(vString, "\"some-string\"");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, tPath)
|
||||
{
|
||||
Value vPath;
|
||||
vPath.mkStringNoCopy("/foo");
|
||||
vPath.mkStringNoCopy("/foo"_sds);
|
||||
test(vPath, "\"/foo\"");
|
||||
}
|
||||
|
||||
@@ -267,7 +268,7 @@ struct StringPrintingTests : LibExprTest
|
||||
void test(std::string_view literal, std::string_view expected, unsigned int maxLength, A... args)
|
||||
{
|
||||
Value v;
|
||||
v.mkString(literal);
|
||||
v.mkString(literal, state.mem);
|
||||
|
||||
std::stringstream out;
|
||||
printValue(state, out, v, PrintOptions{.maxStringLength = maxLength});
|
||||
@@ -289,10 +290,10 @@ TEST_F(StringPrintingTests, maxLengthTruncation)
|
||||
TEST_F(ValuePrintingTests, attrsTypeFirst)
|
||||
{
|
||||
Value vType;
|
||||
vType.mkStringNoCopy("puppy");
|
||||
vType.mkStringNoCopy("puppy"_sds);
|
||||
|
||||
Value vApple;
|
||||
vApple.mkStringNoCopy("apple");
|
||||
vApple.mkStringNoCopy("apple"_sds);
|
||||
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.symbols.create("type"), &vType);
|
||||
@@ -333,7 +334,7 @@ TEST_F(ValuePrintingTests, ansiColorsBool)
|
||||
TEST_F(ValuePrintingTests, ansiColorsString)
|
||||
{
|
||||
Value v;
|
||||
v.mkStringNoCopy("puppy");
|
||||
v.mkStringNoCopy("puppy"_sds);
|
||||
|
||||
test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
@@ -341,7 +342,7 @@ TEST_F(ValuePrintingTests, ansiColorsString)
|
||||
TEST_F(ValuePrintingTests, ansiColorsStringElided)
|
||||
{
|
||||
Value v;
|
||||
v.mkStringNoCopy("puppy");
|
||||
v.mkStringNoCopy("puppy"_sds);
|
||||
|
||||
test(
|
||||
v,
|
||||
@@ -352,7 +353,7 @@ TEST_F(ValuePrintingTests, ansiColorsStringElided)
|
||||
TEST_F(ValuePrintingTests, ansiColorsPath)
|
||||
{
|
||||
Value v;
|
||||
v.mkPath(state.rootPath(CanonPath("puppy")));
|
||||
v.mkPath(state.rootPath(CanonPath("puppy")), state.mem);
|
||||
|
||||
test(v, ANSI_GREEN "/puppy" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
@@ -389,7 +390,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
|
||||
TEST_F(ValuePrintingTests, ansiColorsDerivation)
|
||||
{
|
||||
Value vDerivation;
|
||||
vDerivation.mkStringNoCopy("derivation");
|
||||
vDerivation.mkStringNoCopy("derivation"_sds);
|
||||
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.s.type, &vDerivation);
|
||||
@@ -412,7 +413,7 @@ TEST_F(ValuePrintingTests, ansiColorsError)
|
||||
{
|
||||
Value throw_ = state.getBuiltin("throw");
|
||||
Value message;
|
||||
message.mkStringNoCopy("uh oh!");
|
||||
message.mkStringNoCopy("uh oh!"_sds);
|
||||
Value vError;
|
||||
vError.mkApp(&throw_, &message);
|
||||
|
||||
@@ -429,12 +430,12 @@ TEST_F(ValuePrintingTests, ansiColorsDerivationError)
|
||||
{
|
||||
Value throw_ = state.getBuiltin("throw");
|
||||
Value message;
|
||||
message.mkStringNoCopy("uh oh!");
|
||||
message.mkStringNoCopy("uh oh!"_sds);
|
||||
Value vError;
|
||||
vError.mkApp(&throw_, &message);
|
||||
|
||||
Value vDerivation;
|
||||
vDerivation.mkStringNoCopy("derivation");
|
||||
vDerivation.mkStringNoCopy("derivation"_sds);
|
||||
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.s.type, &vDerivation);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "nix/expr/value.hh"
|
||||
#include "nix/expr/static-string-data.hh"
|
||||
|
||||
#include "nix/store/tests/libstore.hh"
|
||||
#include <gtest/gtest.h>
|
||||
@@ -27,17 +28,17 @@ TEST_F(ValueTest, staticString)
|
||||
{
|
||||
Value vStr1;
|
||||
Value vStr2;
|
||||
vStr1.mkStringNoCopy("foo");
|
||||
vStr2.mkStringNoCopy("foo");
|
||||
vStr1.mkStringNoCopy("foo"_sds);
|
||||
vStr2.mkStringNoCopy("foo"_sds);
|
||||
|
||||
auto sd1 = vStr1.string_view();
|
||||
auto sd2 = vStr2.string_view();
|
||||
auto & sd1 = vStr1.string_data();
|
||||
auto & sd2 = vStr2.string_data();
|
||||
|
||||
// The strings should be the same
|
||||
ASSERT_EQ(sd1, sd2);
|
||||
ASSERT_EQ(sd1.view(), sd2.view());
|
||||
|
||||
// The strings should also be backed by the same (static) allocation
|
||||
ASSERT_EQ(sd1.data(), sd2.data());
|
||||
ASSERT_EQ(&sd1, &sd2);
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -147,7 +147,7 @@ struct AttrDb
|
||||
for (auto * elem : *context) {
|
||||
if (!first)
|
||||
ctx.push_back(' ');
|
||||
ctx.append(elem);
|
||||
ctx.append(elem->view());
|
||||
first = false;
|
||||
}
|
||||
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
|
||||
|
||||
@@ -60,18 +60,18 @@ EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lo
|
||||
Strings EvalSettings::getDefaultNixPath()
|
||||
{
|
||||
Strings res;
|
||||
auto add = [&](const Path & p, const std::string & s = std::string()) {
|
||||
auto add = [&](const std::filesystem::path & p, const std::string & s = std::string()) {
|
||||
if (std::filesystem::exists(p)) {
|
||||
if (s.empty()) {
|
||||
res.push_back(p);
|
||||
res.push_back(p.string());
|
||||
} else {
|
||||
res.push_back(s + "=" + p);
|
||||
res.push_back(s + "=" + p.string());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
add(getNixDefExpr() + "/channels");
|
||||
add(rootChannelsDir() + "/nixpkgs", "nixpkgs");
|
||||
add(std::filesystem::path{getNixDefExpr()} / "channels");
|
||||
add(rootChannelsDir() / "nixpkgs", "nixpkgs");
|
||||
add(rootChannelsDir());
|
||||
|
||||
return res;
|
||||
@@ -108,4 +108,4 @@ Path getNixDefExpr()
|
||||
return settings.useXDGBaseDirectories ? getStateDir() + "/defexpr" : getHome() + "/.nix-defexpr";
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
} // namespace nix
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "nix/expr/primops.hh"
|
||||
#include "nix/expr/print-options.hh"
|
||||
#include "nix/expr/symbol-table.hh"
|
||||
#include "nix/expr/value.hh"
|
||||
#include "nix/util/exit.hh"
|
||||
#include "nix/util/types.hh"
|
||||
#include "nix/util/util.hh"
|
||||
@@ -28,6 +29,8 @@
|
||||
#include "parser-tab.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
@@ -48,6 +51,9 @@ using json = nlohmann::json;
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Just for doc strings. Not for regular string values.
|
||||
*/
|
||||
static char * allocString(size_t size)
|
||||
{
|
||||
char * t;
|
||||
@@ -61,6 +67,9 @@ static char * allocString(size_t size)
|
||||
// string allocations.
|
||||
// This function handles makeImmutableString(std::string_view()) by returning
|
||||
// the empty string.
|
||||
/**
|
||||
* Just for doc strings. Not for regular string values.
|
||||
*/
|
||||
static const char * makeImmutableString(std::string_view s)
|
||||
{
|
||||
const size_t size = s.size();
|
||||
@@ -72,6 +81,25 @@ static const char * makeImmutableString(std::string_view s)
|
||||
return t;
|
||||
}
|
||||
|
||||
StringData & StringData::alloc(EvalMemory & mem, size_t size)
|
||||
{
|
||||
void * t = mem.allocBytes(sizeof(StringData) + size + 1);
|
||||
if (!t)
|
||||
throw std::bad_alloc();
|
||||
auto res = new (t) StringData(size);
|
||||
return *res;
|
||||
}
|
||||
|
||||
const StringData & StringData::make(EvalMemory & mem, std::string_view s)
|
||||
{
|
||||
if (s.empty())
|
||||
return ""_sds;
|
||||
auto & res = alloc(mem, s.size());
|
||||
std::memcpy(&res.data_, s.data(), s.size());
|
||||
res.data_[s.size()] = '\0';
|
||||
return res;
|
||||
}
|
||||
|
||||
RootValue allocRootValue(Value * v)
|
||||
{
|
||||
return std::allocate_shared<Value *>(traceable_allocator<Value *>(), v);
|
||||
@@ -177,7 +205,7 @@ bool Value::isTrivial() const
|
||||
{
|
||||
return !isa<tApp, tPrimOpApp>()
|
||||
&& (!isa<tThunk>()
|
||||
|| (dynamic_cast<ExprAttrs *>(thunk().expr) && ((ExprAttrs *) thunk().expr)->dynamicAttrs.empty())
|
||||
|| (dynamic_cast<ExprAttrs *>(thunk().expr) && ((ExprAttrs *) thunk().expr)->dynamicAttrs->empty())
|
||||
|| dynamic_cast<ExprLambda *>(thunk().expr) || dynamic_cast<ExprList *>(thunk().expr));
|
||||
}
|
||||
|
||||
@@ -489,15 +517,16 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
||||
if (primOp.arity == 0) {
|
||||
primOp.arity = 1;
|
||||
auto vPrimOp = allocValue();
|
||||
vPrimOp->mkPrimOp(new PrimOp(primOp));
|
||||
vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
|
||||
Value v;
|
||||
v.mkApp(vPrimOp, vPrimOp);
|
||||
auto & primOp1 = *vPrimOp->primOp();
|
||||
return addConstant(
|
||||
primOp.name,
|
||||
primOp1.name,
|
||||
v,
|
||||
{
|
||||
.type = nThunk, // FIXME
|
||||
.doc = primOp.doc,
|
||||
.doc = primOp1.doc ? primOp1.doc->c_str() : nullptr,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -537,13 +566,14 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||
{
|
||||
if (v.isPrimOp()) {
|
||||
auto v2 = &v;
|
||||
if (auto * doc = v2->primOp()->doc)
|
||||
auto & primOp = *v2->primOp();
|
||||
if (primOp.doc)
|
||||
return Doc{
|
||||
.pos = {},
|
||||
.name = v2->primOp()->name,
|
||||
.arity = v2->primOp()->arity,
|
||||
.args = v2->primOp()->args,
|
||||
.doc = doc,
|
||||
.name = primOp.name,
|
||||
.arity = primOp.arity,
|
||||
.args = primOp.args,
|
||||
.doc = primOp.doc->c_str(),
|
||||
};
|
||||
}
|
||||
if (v.isLambda()) {
|
||||
@@ -585,7 +615,9 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||
.name = name,
|
||||
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
|
||||
.args = {},
|
||||
.doc = makeImmutableString(s.view()), // NOTE: memory leak when compiled without GC
|
||||
/* N.B. Can't use StringData here, because that would lead to an interior pointer.
|
||||
NOTE: memory leak when compiled without GC. */
|
||||
.doc = makeImmutableString(s.view()),
|
||||
};
|
||||
}
|
||||
if (isFunctor(v)) {
|
||||
@@ -817,35 +849,36 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)
|
||||
evalState.runDebugRepl(nullptr, trace.env, trace.expr);
|
||||
}
|
||||
|
||||
void Value::mkString(std::string_view s)
|
||||
void Value::mkString(std::string_view s, EvalMemory & mem)
|
||||
{
|
||||
mkStringNoCopy(makeImmutableString(s));
|
||||
mkStringNoCopy(StringData::make(mem, s));
|
||||
}
|
||||
|
||||
Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
|
||||
Value::StringWithContext::Context *
|
||||
Value::StringWithContext::Context::fromBuilder(const NixStringContext & context, EvalMemory & mem)
|
||||
{
|
||||
if (context.empty())
|
||||
return nullptr;
|
||||
|
||||
auto ctx = new (allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
|
||||
auto ctx = new (mem.allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
|
||||
std::ranges::transform(
|
||||
context, ctx->elems, [](const NixStringContextElem & elt) { return makeImmutableString(elt.to_string()); });
|
||||
context, ctx->elems, [&](const NixStringContextElem & elt) { return &StringData::make(mem, elt.to_string()); });
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void Value::mkString(std::string_view s, const NixStringContext & context)
|
||||
void Value::mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem)
|
||||
{
|
||||
mkStringNoCopy(makeImmutableString(s), Value::StringWithContext::Context::fromBuilder(context));
|
||||
mkStringNoCopy(StringData::make(mem, s), Value::StringWithContext::Context::fromBuilder(context, mem));
|
||||
}
|
||||
|
||||
void Value::mkStringMove(const char * s, const NixStringContext & context)
|
||||
void Value::mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem)
|
||||
{
|
||||
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context));
|
||||
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context, mem));
|
||||
}
|
||||
|
||||
void Value::mkPath(const SourcePath & path)
|
||||
void Value::mkPath(const SourcePath & path, EvalMemory & mem)
|
||||
{
|
||||
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
|
||||
mkPath(&*path.accessor, StringData::make(mem, path.path.abs()));
|
||||
}
|
||||
|
||||
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||
@@ -881,9 +914,9 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||
}
|
||||
}
|
||||
|
||||
ListBuilder::ListBuilder(size_t size)
|
||||
ListBuilder::ListBuilder(EvalMemory & mem, size_t size)
|
||||
: size(size)
|
||||
, elems(size <= 2 ? inlineElems : (Value **) allocBytes(size * sizeof(Value *)))
|
||||
, elems(size <= 2 ? inlineElems : (Value **) mem.allocBytes(size * sizeof(Value *)))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -910,7 +943,7 @@ void EvalState::mkPos(Value & v, PosIdx p)
|
||||
auto origin = positions.originOf(p);
|
||||
if (auto path = std::get_if<SourcePath>(&origin)) {
|
||||
auto attrs = buildBindings(3);
|
||||
attrs.alloc(s.file).mkString(path->path.abs());
|
||||
attrs.alloc(s.file).mkString(path->path.abs(), mem);
|
||||
makePositionThunks(*this, p, attrs.alloc(s.line), attrs.alloc(s.column));
|
||||
v.mkAttrs(attrs);
|
||||
} else
|
||||
@@ -923,7 +956,8 @@ void EvalState::mkStorePathString(const StorePath & p, Value & v)
|
||||
store->printStorePath(p),
|
||||
NixStringContext{
|
||||
NixStringContextElem::Opaque{.path = p},
|
||||
});
|
||||
},
|
||||
mem);
|
||||
}
|
||||
|
||||
std::string EvalState::mkOutputStringRaw(
|
||||
@@ -945,7 +979,7 @@ void EvalState::mkOutputString(
|
||||
std::optional<StorePath> optStaticOutputPath,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
value.mkString(mkOutputStringRaw(b, optStaticOutputPath, xpSettings), NixStringContext{b});
|
||||
value.mkString(mkOutputStringRaw(b, optStaticOutputPath, xpSettings), NixStringContext{b}, mem);
|
||||
}
|
||||
|
||||
std::string EvalState::mkSingleDerivedPathStringRaw(const SingleDerivedPath & p)
|
||||
@@ -980,7 +1014,8 @@ void EvalState::mkSingleDerivedPathString(const SingleDerivedPath & p, Value & v
|
||||
mkSingleDerivedPathStringRaw(p),
|
||||
NixStringContext{
|
||||
std::visit([](auto && v) -> NixStringContextElem { return v; }, p),
|
||||
});
|
||||
},
|
||||
mem);
|
||||
}
|
||||
|
||||
Value * Expr::maybeThunk(EvalState & state, Env & env)
|
||||
@@ -1191,26 +1226,26 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
|
||||
|
||||
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
auto bindings = state.buildBindings(attrs.size() + dynamicAttrs.size());
|
||||
auto bindings = state.buildBindings(attrs->size() + dynamicAttrs->size());
|
||||
auto dynamicEnv = &env;
|
||||
bool sort = false;
|
||||
|
||||
if (recursive) {
|
||||
/* Create a new environment that contains the attributes in
|
||||
this `rec'. */
|
||||
Env & env2(state.mem.allocEnv(attrs.size()));
|
||||
Env & env2(state.mem.allocEnv(attrs->size()));
|
||||
env2.up = &env;
|
||||
dynamicEnv = &env2;
|
||||
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr;
|
||||
|
||||
AttrDefs::iterator overrides = attrs.find(state.s.overrides);
|
||||
bool hasOverrides = overrides != attrs.end();
|
||||
AttrDefs::iterator overrides = attrs->find(state.s.overrides);
|
||||
bool hasOverrides = overrides != attrs->end();
|
||||
|
||||
/* The recursive attributes are evaluated in the new
|
||||
environment, while the inherited attributes are evaluated
|
||||
in the original environment. */
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs) {
|
||||
for (auto & i : *attrs) {
|
||||
Value * vAttr;
|
||||
if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
|
||||
vAttr = state.allocValue();
|
||||
@@ -1237,8 +1272,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
"while evaluating the `__overrides` attribute");
|
||||
bindings.grow(state.buildBindings(bindings.capacity() + vOverrides->attrs()->size()));
|
||||
for (auto & i : *vOverrides->attrs()) {
|
||||
AttrDefs::iterator j = attrs.find(i.name);
|
||||
if (j != attrs.end()) {
|
||||
AttrDefs::iterator j = attrs->find(i.name);
|
||||
if (j != attrs->end()) {
|
||||
(*bindings.bindings)[j->second.displ] = i;
|
||||
env2.values[j->second.displ] = i.value;
|
||||
} else
|
||||
@@ -1250,13 +1285,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
else {
|
||||
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
|
||||
for (auto & i : attrs)
|
||||
for (auto & i : *attrs)
|
||||
bindings.insert(
|
||||
i.first, i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), i.second.pos);
|
||||
}
|
||||
|
||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||
for (auto & i : dynamicAttrs) {
|
||||
for (auto & i : *dynamicAttrs) {
|
||||
Value nameVal;
|
||||
i.nameExpr->eval(state, *dynamicEnv, nameVal);
|
||||
state.forceValue(nameVal, i.pos);
|
||||
@@ -1290,7 +1325,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
/* Create a new environment that contains the attributes in this
|
||||
`let'. */
|
||||
Env & env2(state.mem.allocEnv(attrs->attrs.size()));
|
||||
Env & env2(state.mem.allocEnv(attrs->attrs->size()));
|
||||
env2.up = &env;
|
||||
|
||||
Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr;
|
||||
@@ -1299,7 +1334,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
while the inherited attributes are evaluated in the original
|
||||
environment. */
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs->attrs) {
|
||||
for (auto & i : *attrs->attrs) {
|
||||
env2.values[displ++] = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
|
||||
}
|
||||
|
||||
@@ -1716,9 +1751,9 @@ void ExprCall::eval(EvalState & state, Env & env, Value & v)
|
||||
// 4: about 60
|
||||
// 5: under 10
|
||||
// This excluded attrset lambdas (`{...}:`). Contributions of mixed lambdas appears insignificant at ~150 total.
|
||||
SmallValueVector<4> vArgs(args.size());
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
vArgs[i] = args[i]->maybeThunk(state, env);
|
||||
SmallValueVector<4> vArgs(args->size());
|
||||
for (size_t i = 0; i < args->size(); ++i)
|
||||
vArgs[i] = (*args)[i]->maybeThunk(state, env);
|
||||
|
||||
state.callFunction(vFun, vArgs, v, pos);
|
||||
}
|
||||
@@ -2099,21 +2134,21 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
.atPos(pos)
|
||||
.withFrame(env, *this)
|
||||
.debugThrow();
|
||||
std::string result_str;
|
||||
result_str.reserve(sSize);
|
||||
std::string resultStr;
|
||||
resultStr.reserve(sSize);
|
||||
for (const auto & part : strings) {
|
||||
result_str += *part;
|
||||
resultStr += *part;
|
||||
}
|
||||
v.mkPath(state.rootPath(CanonPath(result_str)));
|
||||
v.mkPath(state.rootPath(CanonPath(resultStr)), state.mem);
|
||||
} else {
|
||||
char * result_str = allocString(sSize + 1);
|
||||
char * tmp = result_str;
|
||||
auto & resultStr = StringData::alloc(state.mem, sSize);
|
||||
auto * tmp = resultStr.data();
|
||||
for (const auto & part : strings) {
|
||||
memcpy(tmp, part->data(), part->size());
|
||||
std::memcpy(tmp, part->data(), part->size());
|
||||
tmp += part->size();
|
||||
}
|
||||
*tmp = 0;
|
||||
v.mkStringMove(result_str, context);
|
||||
*tmp = '\0';
|
||||
v.mkStringMove(resultStr, context, state.mem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2153,6 +2188,8 @@ void EvalState::forceValueDeep(Value & v)
|
||||
std::set<const Value *> seen;
|
||||
|
||||
[&, &state(*this)](this const auto & recurse, Value & v) {
|
||||
auto _level = state.addCallDepth(v.determinePos(noPos));
|
||||
|
||||
if (!seen.insert(&v).second)
|
||||
return;
|
||||
|
||||
@@ -2179,8 +2216,15 @@ void EvalState::forceValueDeep(Value & v)
|
||||
}
|
||||
|
||||
else if (v.isList()) {
|
||||
size_t index = 0;
|
||||
for (auto v2 : v.listView())
|
||||
recurse(*v2);
|
||||
try {
|
||||
recurse(*v2);
|
||||
index++;
|
||||
} catch (Error & e) {
|
||||
state.addErrorTrace(e, "while evaluating list element at index %1%", index);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}(v);
|
||||
}
|
||||
@@ -2288,7 +2332,7 @@ void copyContext(const Value & v, NixStringContext & context, const Experimental
|
||||
{
|
||||
if (auto * ctx = v.context())
|
||||
for (auto * elem : *ctx)
|
||||
context.insert(NixStringContextElem::parse(elem, xpSettings));
|
||||
context.insert(NixStringContextElem::parse(elem->view(), xpSettings));
|
||||
}
|
||||
|
||||
std::string_view EvalState::forceString(
|
||||
@@ -2310,7 +2354,7 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
|
||||
error<EvalError>(
|
||||
"the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||
v.string_view(),
|
||||
*v.context()->begin())
|
||||
(*v.context()->begin())->view())
|
||||
.withTrace(pos, errorCtx)
|
||||
.debugThrow();
|
||||
}
|
||||
@@ -3155,7 +3199,7 @@ std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Pat
|
||||
|
||||
if (EvalSettings::isPseudoUrl(value)) {
|
||||
try {
|
||||
auto accessor = fetchers::downloadTarball(store, fetchSettings, EvalSettings::resolvePseudoUrl(value));
|
||||
auto accessor = fetchers::downloadTarball(*store, fetchSettings, EvalSettings::resolvePseudoUrl(value));
|
||||
auto storePath = fetchToStore(fetchSettings, *store, SourcePath(accessor), FetchMode::Copy);
|
||||
return finish(this->storePath(storePath));
|
||||
} catch (Error & e) {
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace nix {
|
||||
* Note: Various places expect the allocated memory to be zeroed.
|
||||
*/
|
||||
[[gnu::always_inline]]
|
||||
inline void * allocBytes(size_t n)
|
||||
inline void * EvalMemory::allocBytes(size_t n)
|
||||
{
|
||||
void * p;
|
||||
#if NIX_USE_BOEHMGC
|
||||
|
||||
@@ -108,7 +108,7 @@ struct PrimOp
|
||||
/**
|
||||
* Optional free-form documentation about the primop.
|
||||
*/
|
||||
const char * doc = nullptr;
|
||||
std::optional<std::string> doc;
|
||||
|
||||
/**
|
||||
* Add a trace item, while calling the `<name>` builtin.
|
||||
@@ -335,6 +335,7 @@ public:
|
||||
EvalMemory & operator=(const EvalMemory &) = delete;
|
||||
EvalMemory & operator=(EvalMemory &&) = delete;
|
||||
|
||||
inline void * allocBytes(size_t n);
|
||||
inline Value * allocValue();
|
||||
inline Env & allocEnv(size_t size);
|
||||
|
||||
@@ -348,7 +349,7 @@ public:
|
||||
ListBuilder buildList(size_t size)
|
||||
{
|
||||
stats.nrListElems += size;
|
||||
return ListBuilder(size);
|
||||
return ListBuilder(*this, size);
|
||||
}
|
||||
|
||||
const Statistics & getStats() const &
|
||||
|
||||
@@ -31,6 +31,7 @@ headers = [ config_pub_h ] + files(
|
||||
'print.hh',
|
||||
'repl-exit-status.hh',
|
||||
'search-path.hh',
|
||||
'static-string-data.hh',
|
||||
'symbol-table.hh',
|
||||
'value-to-json.hh',
|
||||
'value-to-xml.hh',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <memory_resource>
|
||||
#include <algorithm>
|
||||
@@ -11,6 +12,7 @@
|
||||
#include "nix/expr/value.hh"
|
||||
#include "nix/expr/symbol-table.hh"
|
||||
#include "nix/expr/eval-error.hh"
|
||||
#include "nix/expr/static-string-data.hh"
|
||||
#include "nix/util/pos-idx.hh"
|
||||
#include "nix/expr/counter.hh"
|
||||
#include "nix/util/pos-table.hh"
|
||||
@@ -186,22 +188,18 @@ struct ExprString : Expr
|
||||
* This is only for strings already allocated in our polymorphic allocator,
|
||||
* or that live at least that long (e.g. c++ string literals)
|
||||
*/
|
||||
ExprString(const char * s)
|
||||
ExprString(const StringData & s)
|
||||
{
|
||||
v.mkStringNoCopy(s);
|
||||
};
|
||||
|
||||
ExprString(std::pmr::polymorphic_allocator<char> & alloc, std::string_view sv)
|
||||
{
|
||||
auto len = sv.length();
|
||||
if (len == 0) {
|
||||
v.mkStringNoCopy("");
|
||||
if (sv.size() == 0) {
|
||||
v.mkStringNoCopy(""_sds);
|
||||
return;
|
||||
}
|
||||
char * s = alloc.allocate(len + 1);
|
||||
sv.copy(s, len);
|
||||
s[len] = '\0';
|
||||
v.mkStringNoCopy(s);
|
||||
v.mkStringNoCopy(StringData::make(*alloc.resource(), sv));
|
||||
};
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
@@ -216,11 +214,7 @@ struct ExprPath : Expr
|
||||
ExprPath(std::pmr::polymorphic_allocator<char> & alloc, ref<SourceAccessor> accessor, std::string_view sv)
|
||||
: accessor(accessor)
|
||||
{
|
||||
auto len = sv.length();
|
||||
char * s = alloc.allocate(len + 1);
|
||||
sv.copy(s, len);
|
||||
s[len] = '\0';
|
||||
v.mkPath(&*accessor, s);
|
||||
v.mkPath(&*accessor, StringData::make(*alloc.resource(), sv));
|
||||
}
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
@@ -345,7 +339,7 @@ struct ExprOpHasAttr : Expr
|
||||
Expr * e;
|
||||
std::span<AttrName> attrPath;
|
||||
|
||||
ExprOpHasAttr(std::pmr::polymorphic_allocator<char> & alloc, Expr * e, std::vector<AttrName> attrPath)
|
||||
ExprOpHasAttr(std::pmr::polymorphic_allocator<char> & alloc, Expr * e, std::span<AttrName> attrPath)
|
||||
: e(e)
|
||||
, attrPath({alloc.allocate_object<AttrName>(attrPath.size()), attrPath.size()})
|
||||
{
|
||||
@@ -401,9 +395,13 @@ struct ExprAttrs : Expr
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
AttrDefs attrs;
|
||||
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
|
||||
typedef std::pmr::map<Symbol, AttrDef> AttrDefs;
|
||||
/**
|
||||
* attrs will never be null. we use std::optional so that we can call emplace() to re-initialize the value with a
|
||||
* new pmr::map using a different allocator (move assignment will copy into the old allocator)
|
||||
*/
|
||||
std::optional<AttrDefs> attrs;
|
||||
std::unique_ptr<std::pmr::vector<Expr *>> inheritFromExprs;
|
||||
|
||||
struct DynamicAttrDef
|
||||
{
|
||||
@@ -415,13 +413,20 @@ struct ExprAttrs : Expr
|
||||
, pos(pos) {};
|
||||
};
|
||||
|
||||
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||
DynamicAttrDefs dynamicAttrs;
|
||||
typedef std::pmr::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||
/**
|
||||
* dynamicAttrs will never be null. See comment on AttrDefs above.
|
||||
*/
|
||||
std::optional<DynamicAttrDefs> dynamicAttrs;
|
||||
ExprAttrs(const PosIdx & pos)
|
||||
: recursive(false)
|
||||
, pos(pos) {};
|
||||
, pos(pos)
|
||||
, attrs(AttrDefs{})
|
||||
, dynamicAttrs(DynamicAttrDefs{}) {};
|
||||
ExprAttrs()
|
||||
: recursive(false) {};
|
||||
: recursive(false)
|
||||
, attrs(AttrDefs{})
|
||||
, dynamicAttrs(DynamicAttrDefs{}) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
@@ -433,13 +438,14 @@ struct ExprAttrs : Expr
|
||||
std::shared_ptr<const StaticEnv> bindInheritSources(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
Env * buildInheritFromEnv(EvalState & state, Env & up);
|
||||
void showBindings(const SymbolTable & symbols, std::ostream & str) const;
|
||||
void moveDataToAllocator(std::pmr::polymorphic_allocator<char> & alloc);
|
||||
};
|
||||
|
||||
struct ExprList : Expr
|
||||
{
|
||||
std::span<Expr *> elems;
|
||||
|
||||
ExprList(std::pmr::polymorphic_allocator<char> & alloc, std::vector<Expr *> exprs)
|
||||
ExprList(std::pmr::polymorphic_allocator<char> & alloc, std::span<Expr *> exprs)
|
||||
: elems({alloc.allocate_object<Expr *>(exprs.size()), exprs.size()})
|
||||
{
|
||||
std::ranges::copy(exprs, elems.begin());
|
||||
@@ -568,7 +574,7 @@ public:
|
||||
const PosTable & positions,
|
||||
std::pmr::polymorphic_allocator<char> & alloc,
|
||||
PosIdx pos,
|
||||
FormalsBuilder formals,
|
||||
const FormalsBuilder & formals,
|
||||
Expr * body)
|
||||
: ExprLambda(positions, alloc, pos, Symbol(), formals, body) {};
|
||||
|
||||
@@ -587,11 +593,14 @@ public:
|
||||
struct ExprCall : Expr
|
||||
{
|
||||
Expr * fun;
|
||||
std::vector<Expr *> args;
|
||||
/**
|
||||
* args will never be null. See comment on ExprAttrs::AttrDefs below.
|
||||
*/
|
||||
std::optional<std::pmr::vector<Expr *>> args;
|
||||
PosIdx pos;
|
||||
std::optional<PosIdx> cursedOrEndPos; // used during parsing to warn about https://github.com/NixOS/nix/issues/11118
|
||||
|
||||
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
|
||||
ExprCall(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args)
|
||||
: fun(fun)
|
||||
, args(args)
|
||||
, pos(pos)
|
||||
@@ -599,7 +608,7 @@ struct ExprCall : Expr
|
||||
{
|
||||
}
|
||||
|
||||
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos)
|
||||
ExprCall(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args, PosIdx && cursedOrEndPos)
|
||||
: fun(fun)
|
||||
, args(args)
|
||||
, pos(pos)
|
||||
@@ -614,6 +623,7 @@ struct ExprCall : Expr
|
||||
|
||||
virtual void resetCursedOr() override;
|
||||
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) override;
|
||||
void moveDataToAllocator(std::pmr::polymorphic_allocator<char> & alloc);
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@@ -759,7 +769,19 @@ struct ExprConcatStrings : Expr
|
||||
std::pmr::polymorphic_allocator<char> & alloc,
|
||||
const PosIdx & pos,
|
||||
bool forceString,
|
||||
const std::vector<std::pair<PosIdx, Expr *>> & es)
|
||||
std::span<std::pair<PosIdx, Expr *>> es)
|
||||
: pos(pos)
|
||||
, forceString(forceString)
|
||||
, es({alloc.allocate_object<std::pair<PosIdx, Expr *>>(es.size()), es.size()})
|
||||
{
|
||||
std::ranges::copy(es, this->es.begin());
|
||||
};
|
||||
|
||||
ExprConcatStrings(
|
||||
std::pmr::polymorphic_allocator<char> & alloc,
|
||||
const PosIdx & pos,
|
||||
bool forceString,
|
||||
std::initializer_list<std::pair<PosIdx, Expr *>> es)
|
||||
: pos(pos)
|
||||
, forceString(forceString)
|
||||
, es({alloc.allocate_object<std::pair<PosIdx, Expr *>>(es.size()), es.size()})
|
||||
@@ -819,7 +841,7 @@ public:
|
||||
// we define some calls to add explicitly so that the argument can be passed in as initializer lists
|
||||
template<class C>
|
||||
[[gnu::always_inline]]
|
||||
C * add(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
|
||||
C * add(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args)
|
||||
requires(std::same_as<C, ExprCall>)
|
||||
{
|
||||
return alloc.new_object<C>(pos, fun, std::move(args));
|
||||
@@ -827,7 +849,7 @@ public:
|
||||
|
||||
template<class C>
|
||||
[[gnu::always_inline]]
|
||||
C * add(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos)
|
||||
C * add(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args, PosIdx && cursedOrEndPos)
|
||||
requires(std::same_as<C, ExprCall>)
|
||||
{
|
||||
return alloc.new_object<C>(pos, fun, std::move(args), std::move(cursedOrEndPos));
|
||||
@@ -839,7 +861,19 @@ public:
|
||||
add(std::pmr::polymorphic_allocator<char> & alloc,
|
||||
const PosIdx & pos,
|
||||
bool forceString,
|
||||
const std::vector<std::pair<PosIdx, Expr *>> & es)
|
||||
std::span<std::pair<PosIdx, Expr *>> es)
|
||||
requires(std::same_as<C, ExprConcatStrings>)
|
||||
{
|
||||
return alloc.new_object<C>(alloc, pos, forceString, es);
|
||||
}
|
||||
|
||||
template<class C>
|
||||
[[gnu::always_inline]]
|
||||
C *
|
||||
add(std::pmr::polymorphic_allocator<char> & alloc,
|
||||
const PosIdx & pos,
|
||||
bool forceString,
|
||||
std::initializer_list<std::pair<PosIdx, Expr *>> es)
|
||||
requires(std::same_as<C, ExprConcatStrings>)
|
||||
{
|
||||
return alloc.new_object<C>(alloc, pos, forceString, es);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <limits>
|
||||
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/expr/value.hh"
|
||||
#include "nix/expr/static-string-data.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -45,6 +47,79 @@ struct ParserLocation
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This represents a string-like parse that possibly has yet to be constructed.
|
||||
*
|
||||
* Examples:
|
||||
* "foo"
|
||||
* ${"foo" + "bar"}
|
||||
* "foo.bar"
|
||||
* "foo-${a}"
|
||||
*
|
||||
* Using this type allows us to avoid construction altogether in cases where what we actually need is the string
|
||||
* contents. For example in foo."bar.baz", there is no need to construct an AST node for "bar.baz", but we don't know
|
||||
* that until we bubble the value up during parsing and see that it's a node in an AttrPath.
|
||||
*/
|
||||
class ToBeStringyExpr
|
||||
{
|
||||
private:
|
||||
using Raw = std::variant<std::monostate, std::string_view, Expr *>;
|
||||
Raw raw;
|
||||
|
||||
public:
|
||||
ToBeStringyExpr() = default;
|
||||
|
||||
ToBeStringyExpr(std::string_view v)
|
||||
: raw(v)
|
||||
{
|
||||
}
|
||||
|
||||
ToBeStringyExpr(Expr * expr)
|
||||
: raw(expr)
|
||||
{
|
||||
assert(expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the expression and invokes an overloaded functor object \ref f.
|
||||
* If the underlying Expr has a dynamic type of ExprString the overload taking std::string_view
|
||||
* is invoked.
|
||||
*
|
||||
* Used to consistently handle simple StringExpr ${"string"} as non-dynamic attributes.
|
||||
* @see https://github.com/NixOS/nix/issues/14642
|
||||
*/
|
||||
template<class F>
|
||||
void visit(F && f)
|
||||
{
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](std::string_view str) { f(str); },
|
||||
[&](Expr * expr) {
|
||||
ExprString * str = dynamic_cast<ExprString *>(expr);
|
||||
if (str)
|
||||
f(str->v.string_view());
|
||||
else
|
||||
f(expr);
|
||||
},
|
||||
[](std::monostate) { unreachable(); }},
|
||||
raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create an Expr from either an existing Expr or from a string.
|
||||
* Delays the allocation or an AST node in case the parser only cares about string contents.
|
||||
*/
|
||||
Expr * toExpr(Exprs & exprs)
|
||||
{
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[&](std::string_view str) -> Expr * { return exprs.add<ExprString>(exprs.alloc, str); },
|
||||
[&](Expr * expr) { return expr; },
|
||||
[](std::monostate) -> Expr * { unreachable(); }},
|
||||
raw);
|
||||
}
|
||||
};
|
||||
|
||||
struct LexerState
|
||||
{
|
||||
/**
|
||||
@@ -94,7 +169,7 @@ struct ParserState
|
||||
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
|
||||
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
|
||||
void validateFormals(FormalsBuilder & formals, PosIdx pos = noPos, Symbol arg = {});
|
||||
Expr * stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
||||
Expr * stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std::variant<Expr *, StringToken>>> es);
|
||||
PosIdx at(const ParserLocation & loc);
|
||||
};
|
||||
|
||||
@@ -124,8 +199,8 @@ inline void ParserState::addAttr(
|
||||
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
|
||||
ExprAttrs * nested;
|
||||
if (i->symbol) {
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(i->symbol);
|
||||
if (j != attrs->attrs->end()) {
|
||||
nested = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||
if (!nested) {
|
||||
attrPath.erase(i + 1, attrPath.end());
|
||||
@@ -133,11 +208,11 @@ inline void ParserState::addAttr(
|
||||
}
|
||||
} else {
|
||||
nested = exprs.add<ExprAttrs>();
|
||||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
||||
(*attrs->attrs)[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
||||
}
|
||||
} else {
|
||||
nested = exprs.add<ExprAttrs>();
|
||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
|
||||
attrs->dynamicAttrs->push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
|
||||
}
|
||||
attrs = nested;
|
||||
}
|
||||
@@ -146,7 +221,7 @@ inline void ParserState::addAttr(
|
||||
if (i->symbol) {
|
||||
addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos));
|
||||
} else {
|
||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
|
||||
attrs->dynamicAttrs->push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
|
||||
}
|
||||
|
||||
auto it = lexerState.positionToDocComment.find(pos);
|
||||
@@ -163,8 +238,8 @@ inline void ParserState::addAttr(
|
||||
inline void
|
||||
ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
|
||||
{
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(symbol);
|
||||
if (j != attrs->attrs->end()) {
|
||||
// This attr path is already defined. However, if both
|
||||
// e and the expr pointed by the attr path are two attribute sets,
|
||||
// we want to merge them.
|
||||
@@ -179,8 +254,8 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
|
||||
// See https://github.com/NixOS/nix/issues/9020.
|
||||
if (jAttrs && ae) {
|
||||
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
|
||||
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
|
||||
for (auto & ad : ae->attrs) {
|
||||
jAttrs->inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>();
|
||||
for (auto & ad : *ae->attrs) {
|
||||
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
|
||||
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
|
||||
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
|
||||
@@ -190,12 +265,12 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
|
||||
addAttr(jAttrs, attrPath, ad.first, std::move(ad.second));
|
||||
attrPath.pop_back();
|
||||
}
|
||||
ae->attrs.clear();
|
||||
jAttrs->dynamicAttrs.insert(
|
||||
jAttrs->dynamicAttrs.end(),
|
||||
std::make_move_iterator(ae->dynamicAttrs.begin()),
|
||||
std::make_move_iterator(ae->dynamicAttrs.end()));
|
||||
ae->dynamicAttrs.clear();
|
||||
ae->attrs->clear();
|
||||
jAttrs->dynamicAttrs->insert(
|
||||
jAttrs->dynamicAttrs->end(),
|
||||
std::make_move_iterator(ae->dynamicAttrs->begin()),
|
||||
std::make_move_iterator(ae->dynamicAttrs->end()));
|
||||
ae->dynamicAttrs->clear();
|
||||
if (ae->inheritFromExprs) {
|
||||
jAttrs->inheritFromExprs->insert(
|
||||
jAttrs->inheritFromExprs->end(),
|
||||
@@ -208,7 +283,7 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
|
||||
}
|
||||
} else {
|
||||
// This attr path is not defined. Let's create it.
|
||||
attrs->attrs.emplace(symbol, def);
|
||||
attrs->attrs->emplace(symbol, def);
|
||||
def.e->setName(symbol);
|
||||
}
|
||||
}
|
||||
@@ -237,10 +312,10 @@ inline void ParserState::validateFormals(FormalsBuilder & formals, PosIdx pos, S
|
||||
}
|
||||
|
||||
inline Expr *
|
||||
ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||
ParserState::stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std::variant<Expr *, StringToken>>> es)
|
||||
{
|
||||
if (es.empty())
|
||||
return exprs.add<ExprString>("");
|
||||
return exprs.add<ExprString>(""_sds);
|
||||
|
||||
/* Figure out the minimum indentation. Note that by design
|
||||
whitespace-only final lines are not taken into account. (So
|
||||
@@ -332,7 +407,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, st
|
||||
// If there is nothing at all, return the empty string directly.
|
||||
// This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
|
||||
if (es2.size() == 0) {
|
||||
auto * const result = exprs.add<ExprString>("");
|
||||
auto * const result = exprs.add<ExprString>(""_sds);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -341,7 +416,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, st
|
||||
auto * const result = (es2)[0].second;
|
||||
return result;
|
||||
}
|
||||
return exprs.add<ExprConcatStrings>(exprs.alloc, pos, true, std::move(es2));
|
||||
return exprs.add<ExprConcatStrings>(exprs.alloc, pos, true, es2);
|
||||
}
|
||||
|
||||
inline PosIdx LexerState::at(const ParserLocation & loc)
|
||||
|
||||
@@ -12,11 +12,7 @@ struct RegisterPrimOp
|
||||
{
|
||||
typedef std::vector<PrimOp> PrimOps;
|
||||
|
||||
static PrimOps & primOps()
|
||||
{
|
||||
static PrimOps primOps;
|
||||
return primOps;
|
||||
}
|
||||
static PrimOps & primOps();
|
||||
|
||||
/**
|
||||
* You can register a constant by passing an arity of 0. fun
|
||||
|
||||
@@ -110,7 +110,7 @@ struct PrintOptions
|
||||
* `PrintOptions` for unknown and therefore potentially large values in error messages,
|
||||
* to avoid printing "too much" output.
|
||||
*/
|
||||
static PrintOptions errorPrintOptions = PrintOptions{
|
||||
static constexpr PrintOptions errorPrintOptions = PrintOptions{
|
||||
.ansiColors = true,
|
||||
.maxDepth = 10,
|
||||
.maxAttrs = 10,
|
||||
|
||||
44
src/libexpr/include/nix/expr/static-string-data.hh
Normal file
44
src/libexpr/include/nix/expr/static-string-data.hh
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "nix/expr/value.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
template<size_t N>
|
||||
struct StringData::Static
|
||||
{
|
||||
/**
|
||||
* @note Must be first to make layout compatible with StringData.
|
||||
*/
|
||||
const size_t size = N - 1;
|
||||
char data[N];
|
||||
|
||||
consteval Static(const char (&str)[N])
|
||||
{
|
||||
static_assert(N > 0);
|
||||
if (str[size] != '\0')
|
||||
throw;
|
||||
std::copy_n(str, N, data);
|
||||
}
|
||||
|
||||
operator const StringData &() const &
|
||||
{
|
||||
static_assert(sizeof(decltype(*this)) >= sizeof(StringData));
|
||||
static_assert(alignof(decltype(*this)) == alignof(StringData));
|
||||
/* NOTE: This cast is somewhat on the fence of what's legal in C++.
|
||||
The question boils down to whether flexible array members are
|
||||
layout compatible with fixed-size arrays. This is a gray area, since
|
||||
FAMs are not standard anyway.
|
||||
*/
|
||||
return *reinterpret_cast<const StringData *>(this);
|
||||
}
|
||||
};
|
||||
|
||||
template<StringData::Static S>
|
||||
const StringData & operator""_sds()
|
||||
{
|
||||
return S;
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <memory_resource>
|
||||
#include "nix/expr/value.hh"
|
||||
#include "nix/expr/static-string-data.hh"
|
||||
#include "nix/util/chunked-vector.hh"
|
||||
#include "nix/util/error.hh"
|
||||
|
||||
@@ -16,7 +17,6 @@ class SymbolValue : protected Value
|
||||
friend class SymbolStr;
|
||||
friend class SymbolTable;
|
||||
|
||||
uint32_t size_;
|
||||
uint32_t idx;
|
||||
|
||||
SymbolValue() = default;
|
||||
@@ -24,7 +24,7 @@ class SymbolValue : protected Value
|
||||
public:
|
||||
operator std::string_view() const noexcept
|
||||
{
|
||||
return {c_str(), size_};
|
||||
return string_view();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,13 +96,13 @@ class SymbolStr
|
||||
SymbolValueStore & store;
|
||||
std::string_view s;
|
||||
std::size_t hash;
|
||||
std::pmr::polymorphic_allocator<char> & alloc;
|
||||
std::pmr::memory_resource & resource;
|
||||
|
||||
Key(SymbolValueStore & store, std::string_view s, std::pmr::polymorphic_allocator<char> & stringAlloc)
|
||||
Key(SymbolValueStore & store, std::string_view s, std::pmr::memory_resource & stringMemory)
|
||||
: store(store)
|
||||
, s(s)
|
||||
, hash(HashType{}(s))
|
||||
, alloc(stringAlloc)
|
||||
, resource(stringMemory)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -122,14 +122,10 @@ public:
|
||||
// for multi-threaded implementations: lock store and allocator here
|
||||
const auto & [v, idx] = key.store.add(SymbolValue{});
|
||||
if (size == 0) {
|
||||
v.mkStringNoCopy("", nullptr);
|
||||
v.mkStringNoCopy(""_sds, nullptr);
|
||||
} else {
|
||||
auto s = key.alloc.allocate(size + 1);
|
||||
memcpy(s, key.s.data(), size);
|
||||
s[size] = '\0';
|
||||
v.mkStringNoCopy(s, nullptr);
|
||||
v.mkStringNoCopy(StringData::make(key.resource, key.s));
|
||||
}
|
||||
v.size_ = size;
|
||||
v.idx = idx;
|
||||
this->s = &v;
|
||||
}
|
||||
@@ -139,6 +135,12 @@ public:
|
||||
return *s == s2;
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
const StringData & string_data() const noexcept
|
||||
{
|
||||
return s->string_data();
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
const char * c_str() const noexcept
|
||||
{
|
||||
@@ -155,13 +157,17 @@ public:
|
||||
[[gnu::always_inline]]
|
||||
bool empty() const noexcept
|
||||
{
|
||||
return s->size_ == 0;
|
||||
auto * p = &s->string_data();
|
||||
// Save a dereference in the sentinel value case
|
||||
if (p == &""_sds)
|
||||
return true;
|
||||
return p->size() == 0;
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
size_t size() const noexcept
|
||||
{
|
||||
return s->size_;
|
||||
return s->string_data().size();
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
@@ -259,7 +265,6 @@ private:
|
||||
* During its lifetime the monotonic buffer holds all strings and nodes, if the symbol set is node based.
|
||||
*/
|
||||
std::pmr::monotonic_buffer_resource buffer;
|
||||
std::pmr::polymorphic_allocator<char> stringAlloc{&buffer};
|
||||
SymbolStr::SymbolValueStore store{16};
|
||||
|
||||
/**
|
||||
@@ -282,7 +287,7 @@ public:
|
||||
// Most symbols are looked up more than once, so we trade off insertion performance
|
||||
// for lookup performance.
|
||||
// FIXME: make this thread-safe.
|
||||
return Symbol(*symbols.insert(SymbolStr::Key{store, s, stringAlloc}).first);
|
||||
return Symbol(*symbols.insert(SymbolStr::Key{store, s, buffer}).first);
|
||||
}
|
||||
|
||||
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <concepts>
|
||||
|
||||
@@ -82,6 +88,7 @@ class PosIdx;
|
||||
struct Pos;
|
||||
class StorePath;
|
||||
class EvalState;
|
||||
class EvalMemory;
|
||||
class XMLWriter;
|
||||
class Printer;
|
||||
|
||||
@@ -155,7 +162,7 @@ class ListBuilder
|
||||
Value * inlineElems[2] = {nullptr, nullptr};
|
||||
public:
|
||||
Value ** elems;
|
||||
ListBuilder(size_t size);
|
||||
ListBuilder(EvalMemory & mem, size_t size);
|
||||
|
||||
// NOTE: Can be noexcept because we are just copying integral values and
|
||||
// raw pointers.
|
||||
@@ -186,6 +193,91 @@ public:
|
||||
friend struct Value;
|
||||
};
|
||||
|
||||
class StringData
|
||||
{
|
||||
public:
|
||||
using size_type = std::size_t;
|
||||
|
||||
size_type size_;
|
||||
char data_[];
|
||||
|
||||
/*
|
||||
* This in particular ensures that we cannot have a `StringData`
|
||||
* that we use by value, which is just what we want!
|
||||
*
|
||||
* Dynamically sized types aren't a thing in C++ and even flexible array
|
||||
* members are a language extension and beyond the realm of standard C++.
|
||||
* Technically, sizeof data_ member is 0 and the intended way to use flexible
|
||||
* array members is to allocate sizeof(StrindData) + count * sizeof(char) bytes
|
||||
* and the compiler will consider alignment restrictions for the FAM.
|
||||
*
|
||||
*/
|
||||
|
||||
StringData(StringData &&) = delete;
|
||||
StringData & operator=(StringData &&) = delete;
|
||||
StringData(const StringData &) = delete;
|
||||
StringData & operator=(const StringData &) = delete;
|
||||
~StringData() = default;
|
||||
|
||||
private:
|
||||
StringData() = delete;
|
||||
|
||||
explicit StringData(size_type size)
|
||||
: size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Allocate StringData on the (possibly) GC-managed heap and copy
|
||||
* the contents of s to it.
|
||||
*/
|
||||
static const StringData & make(EvalMemory & mem, std::string_view s);
|
||||
|
||||
/**
|
||||
* Allocate StringData on the (possibly) GC-managed heap.
|
||||
* @param size Length of the string (without the NUL terminator).
|
||||
*/
|
||||
static StringData & alloc(EvalMemory & mem, size_t size);
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
char * data() noexcept
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const char * data() const noexcept
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const char * c_str() const noexcept
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
constexpr std::string_view view() const noexcept
|
||||
{
|
||||
return std::string_view(data_, size_);
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
struct Static;
|
||||
|
||||
static StringData & make(std::pmr::memory_resource & resource, std::string_view s)
|
||||
{
|
||||
auto & res =
|
||||
*new (resource.allocate(sizeof(StringData) + s.size() + 1, alignof(StringData))) StringData(s.size());
|
||||
std::memcpy(res.data_, s.data(), s.size());
|
||||
res.data_[s.size()] = '\0';
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
@@ -219,7 +311,7 @@ struct ValueBase
|
||||
*/
|
||||
struct StringWithContext
|
||||
{
|
||||
const char * c_str;
|
||||
const StringData * str;
|
||||
|
||||
/**
|
||||
* The type of the context itself.
|
||||
@@ -234,7 +326,7 @@ struct ValueBase
|
||||
*/
|
||||
struct Context
|
||||
{
|
||||
using value_type = const char *;
|
||||
using value_type = const StringData *;
|
||||
using size_type = std::size_t;
|
||||
using iterator = const value_type *;
|
||||
|
||||
@@ -273,7 +365,7 @@ struct ValueBase
|
||||
/**
|
||||
* @return null pointer when context.empty()
|
||||
*/
|
||||
static Context * fromBuilder(const NixStringContext & context);
|
||||
static Context * fromBuilder(const NixStringContext & context, EvalMemory & mem);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -285,7 +377,7 @@ struct ValueBase
|
||||
struct Path
|
||||
{
|
||||
SourceAccessor * accessor;
|
||||
const char * path;
|
||||
const StringData * path;
|
||||
};
|
||||
|
||||
struct Null
|
||||
@@ -646,13 +738,13 @@ protected:
|
||||
void getStorage(StringWithContext & string) const noexcept
|
||||
{
|
||||
string.context = untagPointer<decltype(string.context)>(payload[0]);
|
||||
string.c_str = std::bit_cast<const char *>(payload[1]);
|
||||
string.str = std::bit_cast<const StringData *>(payload[1]);
|
||||
}
|
||||
|
||||
void getStorage(Path & path) const noexcept
|
||||
{
|
||||
path.accessor = untagPointer<decltype(path.accessor)>(payload[0]);
|
||||
path.path = std::bit_cast<const char *>(payload[1]);
|
||||
path.path = std::bit_cast<const StringData *>(payload[1]);
|
||||
}
|
||||
|
||||
void setStorage(NixInt integer) noexcept
|
||||
@@ -697,7 +789,7 @@ protected:
|
||||
|
||||
void setStorage(StringWithContext string) noexcept
|
||||
{
|
||||
setUntaggablePayload<pdString>(string.context, string.c_str);
|
||||
setUntaggablePayload<pdString>(string.context, string.str);
|
||||
}
|
||||
|
||||
void setStorage(Path path) noexcept
|
||||
@@ -1050,22 +1142,22 @@ public:
|
||||
setStorage(b);
|
||||
}
|
||||
|
||||
void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept
|
||||
void mkStringNoCopy(const StringData & s, const Value::StringWithContext::Context * context = nullptr) noexcept
|
||||
{
|
||||
setStorage(StringWithContext{.c_str = s, .context = context});
|
||||
setStorage(StringWithContext{.str = &s, .context = context});
|
||||
}
|
||||
|
||||
void mkString(std::string_view s);
|
||||
void mkString(std::string_view s, EvalMemory & mem);
|
||||
|
||||
void mkString(std::string_view s, const NixStringContext & context);
|
||||
void mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem);
|
||||
|
||||
void mkStringMove(const char * s, const NixStringContext & context);
|
||||
void mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem);
|
||||
|
||||
void mkPath(const SourcePath & path);
|
||||
void mkPath(const SourcePath & path, EvalMemory & mem);
|
||||
|
||||
inline void mkPath(SourceAccessor * accessor, const char * path) noexcept
|
||||
inline void mkPath(SourceAccessor * accessor, const StringData & path) noexcept
|
||||
{
|
||||
setStorage(Path{.accessor = accessor, .path = path});
|
||||
setStorage(Path{.accessor = accessor, .path = &path});
|
||||
}
|
||||
|
||||
inline void mkNull() noexcept
|
||||
@@ -1163,17 +1255,23 @@ public:
|
||||
|
||||
SourcePath path() const
|
||||
{
|
||||
return SourcePath(ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), pathStr()));
|
||||
return SourcePath(
|
||||
ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), std::string(pathStrView())));
|
||||
}
|
||||
|
||||
std::string_view string_view() const noexcept
|
||||
const StringData & string_data() const noexcept
|
||||
{
|
||||
return std::string_view{getStorage<StringWithContext>().c_str};
|
||||
return *getStorage<StringWithContext>().str;
|
||||
}
|
||||
|
||||
const char * c_str() const noexcept
|
||||
{
|
||||
return getStorage<StringWithContext>().c_str;
|
||||
return getStorage<StringWithContext>().str->data();
|
||||
}
|
||||
|
||||
std::string_view string_view() const noexcept
|
||||
{
|
||||
return string_data().view();
|
||||
}
|
||||
|
||||
const Value::StringWithContext::Context * context() const noexcept
|
||||
@@ -1233,12 +1331,12 @@ public:
|
||||
|
||||
const char * pathStr() const noexcept
|
||||
{
|
||||
return getStorage<Path>().path;
|
||||
return getStorage<Path>().path->c_str();
|
||||
}
|
||||
|
||||
std::string_view pathStrView() const noexcept
|
||||
{
|
||||
return std::string_view{getStorage<Path>().path};
|
||||
return getStorage<Path>().path->view();
|
||||
}
|
||||
|
||||
SourceAccessor * pathAccessor() const noexcept
|
||||
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
bool string(string_t & val) override
|
||||
{
|
||||
forceNoNullByte(val);
|
||||
rs->value(state).mkString(val);
|
||||
rs->value(state).mkString(val, state.mem);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,9 +74,9 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
|
||||
void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
typedef const decltype(attrs)::value_type * Attr;
|
||||
typedef const AttrDefs::value_type * Attr;
|
||||
std::vector<Attr> sorted;
|
||||
for (auto & i : attrs)
|
||||
for (auto & i : *attrs)
|
||||
sorted.push_back(&i);
|
||||
std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
|
||||
std::string_view sa = symbols[a->first], sb = symbols[b->first];
|
||||
@@ -122,7 +122,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
||||
str << "; ";
|
||||
}
|
||||
}
|
||||
for (auto & i : dynamicAttrs) {
|
||||
for (auto & i : *dynamicAttrs) {
|
||||
str << "\"${";
|
||||
i.nameExpr->show(symbols, str);
|
||||
str << "}\" = ";
|
||||
@@ -191,7 +191,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
str << '(';
|
||||
fun->show(symbols, str);
|
||||
for (auto e : args) {
|
||||
for (auto e : *args) {
|
||||
str << ' ';
|
||||
e->show(symbols, str);
|
||||
}
|
||||
@@ -399,17 +399,29 @@ ExprAttrs::bindInheritSources(EvalState & es, const std::shared_ptr<const Static
|
||||
return inner;
|
||||
}
|
||||
|
||||
void ExprAttrs::moveDataToAllocator(std::pmr::polymorphic_allocator<char> & alloc)
|
||||
{
|
||||
AttrDefs newAttrs{std::move(*attrs), alloc};
|
||||
attrs.emplace(std::move(newAttrs), alloc);
|
||||
DynamicAttrDefs newDynamicAttrs{std::move(*dynamicAttrs), alloc};
|
||||
dynamicAttrs.emplace(std::move(newDynamicAttrs), alloc);
|
||||
if (inheritFromExprs)
|
||||
inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>(std::move(*inheritFromExprs), alloc);
|
||||
}
|
||||
|
||||
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
moveDataToAllocator(es.mem.exprs.alloc);
|
||||
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
if (recursive) {
|
||||
auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs.size());
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->size());
|
||||
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs)
|
||||
for (auto & i : *attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
return newEnv;
|
||||
}();
|
||||
@@ -417,20 +429,20 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
// No need to sort newEnv since attrs is in sorted order.
|
||||
|
||||
auto inheritFromEnv = bindInheritSources(es, newEnv);
|
||||
for (auto & i : attrs)
|
||||
for (auto & i : *attrs)
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
|
||||
|
||||
for (auto & i : dynamicAttrs) {
|
||||
for (auto & i : *dynamicAttrs) {
|
||||
i.nameExpr->bindVars(es, newEnv);
|
||||
i.valueExpr->bindVars(es, newEnv);
|
||||
}
|
||||
} else {
|
||||
auto inheritFromEnv = bindInheritSources(es, env);
|
||||
|
||||
for (auto & i : attrs)
|
||||
for (auto & i : *attrs)
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv));
|
||||
|
||||
for (auto & i : dynamicAttrs) {
|
||||
for (auto & i : *dynamicAttrs) {
|
||||
i.nameExpr->bindVars(es, env);
|
||||
i.valueExpr->bindVars(es, env);
|
||||
}
|
||||
@@ -473,23 +485,31 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
body->bindVars(es, newEnv);
|
||||
}
|
||||
|
||||
void ExprCall::moveDataToAllocator(std::pmr::polymorphic_allocator<char> & alloc)
|
||||
{
|
||||
std::pmr::vector<Expr *> newArgs{std::move(*args), alloc};
|
||||
args.emplace(std::move(newArgs), alloc);
|
||||
}
|
||||
|
||||
void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
moveDataToAllocator(es.mem.exprs.alloc);
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
fun->bindVars(es, env);
|
||||
for (auto e : args)
|
||||
for (auto e : *args)
|
||||
e->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
attrs->moveDataToAllocator(es.mem.exprs.alloc);
|
||||
auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs.size());
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs->size());
|
||||
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs->attrs)
|
||||
for (auto & i : *attrs->attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
return newEnv;
|
||||
}();
|
||||
@@ -497,7 +517,7 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
// No need to sort newEnv since attrs->attrs is in sorted order.
|
||||
|
||||
auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
|
||||
for (auto & i : attrs->attrs)
|
||||
for (auto & i : *attrs->attrs)
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
|
||||
|
||||
if (es.debugRepl)
|
||||
|
||||
@@ -70,11 +70,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
nix-util
|
||||
nix-store
|
||||
nix-fetchers
|
||||
]
|
||||
++ finalAttrs.passthru.externalPropagatedBuildInputs;
|
||||
|
||||
# Hack for sake of the dev shell
|
||||
passthru.externalPropagatedBuildInputs = [
|
||||
boost
|
||||
nlohmann_json
|
||||
]
|
||||
|
||||
@@ -115,7 +115,7 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P
|
||||
|
||||
static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
|
||||
if (auto e2 = dynamic_cast<ExprCall *>(fn)) {
|
||||
e2->args.push_back(arg);
|
||||
e2->args->push_back(arg);
|
||||
return fn;
|
||||
}
|
||||
return exprs.add<ExprCall>(pos, fn, {arg});
|
||||
@@ -126,26 +126,26 @@ static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
|
||||
|
||||
%define api.value.type variant
|
||||
|
||||
%type <nix::Expr *> start expr expr_function expr_if expr_op
|
||||
%type <nix::Expr *> expr_select expr_simple expr_app
|
||||
%type <nix::Expr *> expr_pipe_from expr_pipe_into
|
||||
%type <std::vector<Expr *>> list
|
||||
%type <nix::ExprAttrs *> binds binds1
|
||||
%type <nix::FormalsBuilder> formals formal_set
|
||||
%type <nix::Formal> formal
|
||||
%type <std::vector<nix::AttrName>> attrpath
|
||||
%type <std::vector<std::pair<nix::AttrName, nix::PosIdx>>> attrs
|
||||
%type <std::vector<std::pair<nix::PosIdx, nix::Expr *>>> string_parts_interpolated
|
||||
%type <std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>>> ind_string_parts
|
||||
%type <nix::Expr *> path_start
|
||||
%type <std::variant<nix::Expr *, std::string_view>> string_parts string_attr
|
||||
%type <nix::StringToken> attr
|
||||
%token <nix::StringToken> ID
|
||||
%token <nix::StringToken> STR IND_STR
|
||||
%token <nix::NixInt> INT_LIT
|
||||
%token <nix::NixFloat> FLOAT_LIT
|
||||
%token <nix::StringToken> PATH HPATH SPATH PATH_END
|
||||
%token <nix::StringToken> URI
|
||||
%type <Expr *> start expr expr_function expr_if expr_op
|
||||
%type <Expr *> expr_select expr_simple expr_app
|
||||
%type <Expr *> expr_pipe_from expr_pipe_into
|
||||
%type <std::pmr::vector<Expr *>> list
|
||||
%type <ExprAttrs *> binds binds1
|
||||
%type <FormalsBuilder> formals formal_set
|
||||
%type <Formal> formal
|
||||
%type <std::vector<AttrName>> attrpath
|
||||
%type <std::vector<std::pair<AttrName, PosIdx>>> attrs
|
||||
%type <std::vector<std::pair<PosIdx, Expr *>>> string_parts_interpolated
|
||||
%type <std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>>> ind_string_parts
|
||||
%type <Expr *> path_start
|
||||
%type <ToBeStringyExpr> string_parts string_attr
|
||||
%type <StringToken> attr
|
||||
%token <StringToken> ID
|
||||
%token <StringToken> STR IND_STR
|
||||
%token <NixInt> INT_LIT
|
||||
%token <NixFloat> FLOAT_LIT
|
||||
%token <StringToken> PATH HPATH SPATH PATH_END
|
||||
%token <StringToken> URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN_KW REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
||||
%token PIPE_FROM PIPE_INTO /* <| and |> */
|
||||
%token DOLLAR_CURLY /* == ${ */
|
||||
@@ -186,7 +186,7 @@ expr_function
|
||||
| formal_set ':' expr_function[body]
|
||||
{
|
||||
state->validateFormals($formal_set);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, std::move($formal_set), $body);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, $formal_set, $body);
|
||||
$$ = me;
|
||||
SET_DOC_POS(me, @1);
|
||||
}
|
||||
@@ -194,7 +194,7 @@ expr_function
|
||||
{
|
||||
auto arg = state->symbols.create($ID);
|
||||
state->validateFormals($formal_set, CUR_POS, arg);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, std::move($formal_set), $body);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, $formal_set, $body);
|
||||
$$ = me;
|
||||
SET_DOC_POS(me, @1);
|
||||
}
|
||||
@@ -202,7 +202,7 @@ expr_function
|
||||
{
|
||||
auto arg = state->symbols.create($ID);
|
||||
state->validateFormals($formal_set, CUR_POS, arg);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, std::move($formal_set), $body);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, $formal_set, $body);
|
||||
$$ = me;
|
||||
SET_DOC_POS(me, @1);
|
||||
}
|
||||
@@ -211,7 +211,7 @@ expr_function
|
||||
| WITH expr ';' expr_function
|
||||
{ $$ = state->exprs.add<ExprWith>(CUR_POS, $2, $4); }
|
||||
| LET binds IN_KW expr_function
|
||||
{ if (!$2->dynamicAttrs.empty())
|
||||
{ if (!$2->dynamicAttrs->empty())
|
||||
throw ParseError({
|
||||
.msg = HintFmt("dynamic attributes not allowed in let"),
|
||||
.pos = state->positions[CUR_POS]
|
||||
@@ -251,7 +251,7 @@ expr_op
|
||||
| expr_op OR expr_op { $$ = state->exprs.add<ExprOpOr>(state->at(@2), $1, $3); }
|
||||
| expr_op IMPL expr_op { $$ = state->exprs.add<ExprOpImpl>(state->at(@2), $1, $3); }
|
||||
| expr_op UPDATE expr_op { $$ = state->exprs.add<ExprOpUpdate>(state->at(@2), $1, $3); }
|
||||
| expr_op '?' attrpath { $$ = state->exprs.add<ExprOpHasAttr>(state->exprs.alloc, $1, std::move($3)); }
|
||||
| expr_op '?' attrpath { $$ = state->exprs.add<ExprOpHasAttr>(state->exprs.alloc, $1, $3); }
|
||||
| expr_op '+' expr_op
|
||||
{ $$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, state->at(@2), false, {{state->at(@1), $1}, {state->at(@3), $3}}); }
|
||||
| expr_op '-' expr_op { $$ = state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.sub), {$1, $3}); }
|
||||
@@ -272,9 +272,9 @@ expr_app
|
||||
|
||||
expr_select
|
||||
: expr_simple '.' attrpath
|
||||
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, std::move($3), nullptr); }
|
||||
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, $3, nullptr); }
|
||||
| expr_simple '.' attrpath OR_KW expr_select
|
||||
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, std::move($3), $5); $5->warnIfCursedOr(state->symbols, state->positions); }
|
||||
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, $3, $5); $5->warnIfCursedOr(state->symbols, state->positions); }
|
||||
| /* Backwards compatibility: because Nixpkgs has a function named ‘or’,
|
||||
allow stuff like ‘map or [...]’. This production is problematic (see
|
||||
https://github.com/NixOS/nix/issues/11118) and will be refactored in the
|
||||
@@ -297,19 +297,14 @@ expr_simple
|
||||
}
|
||||
| INT_LIT { $$ = state->exprs.add<ExprInt>($1); }
|
||||
| FLOAT_LIT { $$ = state->exprs.add<ExprFloat>($1); }
|
||||
| '"' string_parts '"' {
|
||||
std::visit(overloaded{
|
||||
[&](std::string_view str) { $$ = state->exprs.add<ExprString>(state->exprs.alloc, str); },
|
||||
[&](Expr * expr) { $$ = expr; }},
|
||||
$2);
|
||||
}
|
||||
| '"' string_parts '"' { $$ = $2.toExpr(state->exprs); }
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = state->stripIndentation(CUR_POS, std::move($2));
|
||||
$$ = state->stripIndentation(CUR_POS, $2);
|
||||
}
|
||||
| path_start PATH_END
|
||||
| path_start string_parts_interpolated PATH_END {
|
||||
$2.insert($2.begin(), {state->at(@1), $1});
|
||||
$$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, false, std::move($2));
|
||||
$$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, false, $2);
|
||||
}
|
||||
| SPATH {
|
||||
std::string_view path($1.p + 1, $1.l - 2);
|
||||
@@ -338,13 +333,13 @@ expr_simple
|
||||
{ $2->pos = CUR_POS; $$ = $2; }
|
||||
| '{' '}'
|
||||
{ $$ = state->exprs.add<ExprAttrs>(CUR_POS); }
|
||||
| '[' list ']' { $$ = state->exprs.add<ExprList>(state->exprs.alloc, std::move($2)); }
|
||||
| '[' list ']' { $$ = state->exprs.add<ExprList>(state->exprs.alloc, $2); }
|
||||
;
|
||||
|
||||
string_parts
|
||||
: STR { $$ = $1; }
|
||||
| string_parts_interpolated { $$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, true, std::move($1)); }
|
||||
| { $$ = std::string_view(); }
|
||||
: STR { $$ = {$1}; }
|
||||
| string_parts_interpolated { $$ = {state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, true, $1)}; }
|
||||
| { $$ = {std::string_view()}; }
|
||||
;
|
||||
|
||||
string_parts_interpolated
|
||||
@@ -413,9 +408,9 @@ binds1
|
||||
| binds[accum] INHERIT attrs ';'
|
||||
{ $$ = $accum;
|
||||
for (auto & [i, iPos] : $attrs) {
|
||||
if ($accum->attrs.find(i.symbol) != $accum->attrs.end())
|
||||
state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos);
|
||||
$accum->attrs.emplace(
|
||||
if ($accum->attrs->find(i.symbol) != $accum->attrs->end())
|
||||
state->dupAttr(i.symbol, iPos, (*$accum->attrs)[i.symbol].pos);
|
||||
$accum->attrs->emplace(
|
||||
i.symbol,
|
||||
ExprAttrs::AttrDef(state->exprs.add<ExprVar>(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
|
||||
}
|
||||
@@ -423,13 +418,13 @@ binds1
|
||||
| binds[accum] INHERIT '(' expr ')' attrs ';'
|
||||
{ $$ = $accum;
|
||||
if (!$accum->inheritFromExprs)
|
||||
$accum->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
|
||||
$accum->inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>();
|
||||
$accum->inheritFromExprs->push_back($expr);
|
||||
auto from = new nix::ExprInheritFrom(state->at(@expr), $accum->inheritFromExprs->size() - 1);
|
||||
auto from = state->exprs.add<ExprInheritFrom>(state->at(@expr), $accum->inheritFromExprs->size() - 1);
|
||||
for (auto & [i, iPos] : $attrs) {
|
||||
if ($accum->attrs.find(i.symbol) != $accum->attrs.end())
|
||||
state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos);
|
||||
$accum->attrs.emplace(
|
||||
if ($accum->attrs->find(i.symbol) != $accum->attrs->end())
|
||||
state->dupAttr(i.symbol, iPos, (*$accum->attrs)[i.symbol].pos);
|
||||
$accum->attrs->emplace(
|
||||
i.symbol,
|
||||
ExprAttrs::AttrDef(
|
||||
state->exprs.add<ExprSelect>(state->exprs.alloc, iPos, from, i.symbol),
|
||||
@@ -447,15 +442,15 @@ attrs
|
||||
: attrs attr { $$ = std::move($1); $$.emplace_back(state->symbols.create($2), state->at(@2)); }
|
||||
| attrs string_attr
|
||||
{ $$ = std::move($1);
|
||||
std::visit(overloaded {
|
||||
$2.visit(overloaded{
|
||||
[&](std::string_view str) { $$.emplace_back(state->symbols.create(str), state->at(@2)); },
|
||||
[&](Expr * expr) {
|
||||
throw ParseError({
|
||||
.msg = HintFmt("dynamic attributes not allowed in inherit"),
|
||||
.pos = state->positions[state->at(@2)]
|
||||
});
|
||||
}
|
||||
}, $2);
|
||||
throw ParseError({
|
||||
.msg = HintFmt("dynamic attributes not allowed in inherit"),
|
||||
.pos = state->positions[state->at(@2)]
|
||||
});
|
||||
}}
|
||||
);
|
||||
}
|
||||
| { }
|
||||
;
|
||||
@@ -464,17 +459,17 @@ attrpath
|
||||
: attrpath '.' attr { $$ = std::move($1); $$.emplace_back(state->symbols.create($3)); }
|
||||
| attrpath '.' string_attr
|
||||
{ $$ = std::move($1);
|
||||
std::visit(overloaded {
|
||||
$3.visit(overloaded{
|
||||
[&](std::string_view str) { $$.emplace_back(state->symbols.create(str)); },
|
||||
[&](Expr * expr) { $$.emplace_back(expr); }
|
||||
}, std::move($3));
|
||||
[&](Expr * expr) { $$.emplace_back(expr); }}
|
||||
);
|
||||
}
|
||||
| attr { $$.emplace_back(state->symbols.create($1)); }
|
||||
| string_attr
|
||||
{ std::visit(overloaded {
|
||||
{ $1.visit(overloaded{
|
||||
[&](std::string_view str) { $$.emplace_back(state->symbols.create(str)); },
|
||||
[&](Expr * expr) { $$.emplace_back(expr); }
|
||||
}, std::move($1));
|
||||
[&](Expr * expr) { $$.emplace_back(expr); }}
|
||||
);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -485,7 +480,7 @@ attr
|
||||
|
||||
string_attr
|
||||
: '"' string_parts '"' { $$ = std::move($2); }
|
||||
| DOLLAR_CURLY expr '}' { $$ = $2; }
|
||||
| DOLLAR_CURLY expr '}' { $$ = {$2}; }
|
||||
;
|
||||
|
||||
list
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "nix/expr/eval-settings.hh"
|
||||
#include "nix/expr/gc-small-vector.hh"
|
||||
#include "nix/expr/json-to-value.hh"
|
||||
#include "nix/expr/static-string-data.hh"
|
||||
#include "nix/store/globals.hh"
|
||||
#include "nix/store/names.hh"
|
||||
#include "nix/store/path-references.hh"
|
||||
@@ -39,6 +40,12 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
RegisterPrimOp::PrimOps & RegisterPrimOp::primOps()
|
||||
{
|
||||
static RegisterPrimOp::PrimOps primOps;
|
||||
return primOps;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Miscellaneous
|
||||
*************************************************************/
|
||||
@@ -46,7 +53,7 @@ namespace nix {
|
||||
static inline Value * mkString(EvalState & state, const std::csub_match & match)
|
||||
{
|
||||
Value * v = state.allocValue();
|
||||
v->mkString({match.first, match.second});
|
||||
v->mkString({match.first, match.second}, state.mem);
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -221,13 +228,14 @@ void derivationToValue(
|
||||
path2,
|
||||
{
|
||||
NixStringContextElem::DrvDeep{.drvPath = storePath},
|
||||
});
|
||||
attrs.alloc(state.s.name).mkString(drv.env["name"]);
|
||||
},
|
||||
state.mem);
|
||||
attrs.alloc(state.s.name).mkString(drv.env["name"], state.mem);
|
||||
|
||||
auto list = state.buildList(drv.outputs.size());
|
||||
for (const auto & [i, o] : enumerate(drv.outputs)) {
|
||||
mkOutputString(state, attrs, storePath, o);
|
||||
(list[i] = state.allocValue())->mkString(o.first);
|
||||
(list[i] = state.allocValue())->mkString(o.first, state.mem);
|
||||
}
|
||||
attrs.alloc(state.s.outputs).mkList(list);
|
||||
|
||||
@@ -487,34 +495,34 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value ** args, Valu
|
||||
state.forceValue(*args[0], pos);
|
||||
switch (args[0]->type()) {
|
||||
case nInt:
|
||||
v.mkStringNoCopy("int");
|
||||
v.mkStringNoCopy("int"_sds);
|
||||
break;
|
||||
case nBool:
|
||||
v.mkStringNoCopy("bool");
|
||||
v.mkStringNoCopy("bool"_sds);
|
||||
break;
|
||||
case nString:
|
||||
v.mkStringNoCopy("string");
|
||||
v.mkStringNoCopy("string"_sds);
|
||||
break;
|
||||
case nPath:
|
||||
v.mkStringNoCopy("path");
|
||||
v.mkStringNoCopy("path"_sds);
|
||||
break;
|
||||
case nNull:
|
||||
v.mkStringNoCopy("null");
|
||||
v.mkStringNoCopy("null"_sds);
|
||||
break;
|
||||
case nAttrs:
|
||||
v.mkStringNoCopy("set");
|
||||
v.mkStringNoCopy("set"_sds);
|
||||
break;
|
||||
case nList:
|
||||
v.mkStringNoCopy("list");
|
||||
v.mkStringNoCopy("list"_sds);
|
||||
break;
|
||||
case nFunction:
|
||||
v.mkStringNoCopy("lambda");
|
||||
v.mkStringNoCopy("lambda"_sds);
|
||||
break;
|
||||
case nExternal:
|
||||
v.mkString(args[0]->external()->typeOf());
|
||||
v.mkString(args[0]->external()->typeOf(), state.mem);
|
||||
break;
|
||||
case nFloat:
|
||||
v.mkStringNoCopy("float");
|
||||
v.mkStringNoCopy("float"_sds);
|
||||
break;
|
||||
case nThunk:
|
||||
unreachable();
|
||||
@@ -1168,7 +1176,7 @@ static void prim_getEnv(EvalState & state, const PosIdx pos, Value ** args, Valu
|
||||
{
|
||||
std::string name(
|
||||
state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getEnv"));
|
||||
v.mkString(state.settings.restrictEval || state.settings.pureEval ? "" : getEnv(name).value_or(""));
|
||||
v.mkString(state.settings.restrictEval || state.settings.pureEval ? "" : getEnv(name).value_or(""), state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_getEnv({
|
||||
@@ -1766,28 +1774,7 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
|
||||
drv.outputs.insert_or_assign(i, DerivationOutput::Deferred{});
|
||||
}
|
||||
|
||||
auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true);
|
||||
switch (hashModulo.kind) {
|
||||
case DrvHash::Kind::Regular:
|
||||
for (auto & i : outputs) {
|
||||
auto h = get(hashModulo.hashes, i);
|
||||
if (!h)
|
||||
state.error<AssertionError>("derivation produced no hash for output '%s'", i).atPos(v).debugThrow();
|
||||
auto outPath = state.store->makeOutputPath(i, *h, drvName);
|
||||
drv.env[i] = state.store->printStorePath(outPath);
|
||||
drv.outputs.insert_or_assign(
|
||||
i,
|
||||
DerivationOutput::InputAddressed{
|
||||
.path = std::move(outPath),
|
||||
});
|
||||
}
|
||||
break;
|
||||
;
|
||||
case DrvHash::Kind::Deferred:
|
||||
for (auto & i : outputs) {
|
||||
drv.outputs.insert_or_assign(i, DerivationOutput::Deferred{});
|
||||
}
|
||||
}
|
||||
drv.fillInOutputPaths(*state.store);
|
||||
}
|
||||
|
||||
/* Write the resulting term into the Nix store directory. */
|
||||
@@ -1810,7 +1797,8 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
|
||||
drvPathS,
|
||||
{
|
||||
NixStringContextElem::DrvDeep{.drvPath = drvPath},
|
||||
});
|
||||
},
|
||||
state.mem);
|
||||
for (auto & i : drv.outputs)
|
||||
mkOutputString(state, result, drvPath, i);
|
||||
|
||||
@@ -1833,8 +1821,10 @@ static RegisterPrimOp primop_derivationStrict(
|
||||
‘out’. */
|
||||
static void prim_placeholder(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
v.mkString(hashPlaceholder(
|
||||
state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.placeholder")));
|
||||
v.mkString(
|
||||
hashPlaceholder(state.forceStringNoCtx(
|
||||
*args[0], pos, "while evaluating the first argument passed to builtins.placeholder")),
|
||||
state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_placeholder({
|
||||
@@ -1863,7 +1853,7 @@ static void prim_toPath(EvalState & state, const PosIdx pos, Value ** args, Valu
|
||||
NixStringContext context;
|
||||
auto path =
|
||||
state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.toPath");
|
||||
v.mkString(path.path.abs(), context);
|
||||
v.mkString(path.path.abs(), context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_toPath({
|
||||
@@ -1906,7 +1896,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value ** args, V
|
||||
if (!settings.readOnlyMode)
|
||||
state.store->ensurePath(path2);
|
||||
context.insert(NixStringContextElem::Opaque{.path = path2});
|
||||
v.mkString(path.abs(), context);
|
||||
v.mkString(path.abs(), context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_storePath({
|
||||
@@ -1988,7 +1978,8 @@ static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value ** args,
|
||||
v.mkString(
|
||||
legacyBaseNameOf(*state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the first argument passed to builtins.baseNameOf", false, false)),
|
||||
context);
|
||||
context,
|
||||
state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_baseNameOf({
|
||||
@@ -2017,18 +2008,18 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, Value
|
||||
state.forceValue(*args[0], pos);
|
||||
if (args[0]->type() == nPath) {
|
||||
auto path = args[0]->path();
|
||||
v.mkPath(path.path.isRoot() ? path : path.parent());
|
||||
v.mkPath(path.path.isRoot() ? path : path.parent(), state.mem);
|
||||
} else {
|
||||
NixStringContext context;
|
||||
auto path = state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the first argument passed to 'builtins.dirOf'", false, false);
|
||||
auto pos = path->rfind('/');
|
||||
if (pos == path->npos)
|
||||
v.mkStringMove(".", context);
|
||||
v.mkStringMove("."_sds, context, state.mem);
|
||||
else if (pos == 0)
|
||||
v.mkStringMove("/", context);
|
||||
v.mkStringMove("/"_sds, context, state.mem);
|
||||
else
|
||||
v.mkString(path->substr(0, pos), context);
|
||||
v.mkString(path->substr(0, pos), context, state.mem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2070,7 +2061,7 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value ** args, Va
|
||||
.path = std::move((StorePath &&) p),
|
||||
});
|
||||
}
|
||||
v.mkString(s, context);
|
||||
v.mkString(s, context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_readFile({
|
||||
@@ -2134,7 +2125,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value ** args, Va
|
||||
auto path =
|
||||
state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.findFile");
|
||||
|
||||
v.mkPath(state.findFile(lookupPath, path, pos));
|
||||
v.mkPath(state.findFile(lookupPath, path, pos), state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_findFile(
|
||||
@@ -2283,7 +2274,7 @@ static void prim_hashFile(EvalState & state, const PosIdx pos, Value ** args, Va
|
||||
|
||||
auto path = realisePath(state, pos, *args[1]);
|
||||
|
||||
v.mkString(hashString(*ha, path.readFile()).to_string(HashFormat::Base16, false));
|
||||
v.mkString(hashString(*ha, path.readFile()).to_string(HashFormat::Base16, false), state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_hashFile({
|
||||
@@ -2309,10 +2300,10 @@ static const Value & fileTypeToString(EvalState & state, SourceAccessor::Type ty
|
||||
|
||||
static const Constants stringValues = []() {
|
||||
Constants res;
|
||||
res.regular.mkStringNoCopy("regular");
|
||||
res.directory.mkStringNoCopy("directory");
|
||||
res.symlink.mkStringNoCopy("symlink");
|
||||
res.unknown.mkStringNoCopy("unknown");
|
||||
res.regular.mkStringNoCopy("regular"_sds);
|
||||
res.directory.mkStringNoCopy("directory"_sds);
|
||||
res.symlink.mkStringNoCopy("symlink"_sds);
|
||||
res.unknown.mkStringNoCopy("unknown"_sds);
|
||||
return res;
|
||||
}();
|
||||
|
||||
@@ -2372,7 +2363,7 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value ** args, Val
|
||||
// detailed node info quickly in this case we produce a thunk to
|
||||
// query the file type lazily.
|
||||
auto epath = state.allocValue();
|
||||
epath->mkPath(path / name);
|
||||
epath->mkPath(path / name, state.mem);
|
||||
if (!readFileType)
|
||||
readFileType = &state.getBuiltin("readFileType");
|
||||
attr.mkApp(readFileType, epath);
|
||||
@@ -2466,7 +2457,7 @@ static void prim_toXML(EvalState & state, const PosIdx pos, Value ** args, Value
|
||||
std::ostringstream out;
|
||||
NixStringContext context;
|
||||
printValueAsXML(state, true, false, *args[0], out, context, pos);
|
||||
v.mkString(out.view(), context);
|
||||
v.mkString(out.view(), context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_toXML({
|
||||
@@ -2574,7 +2565,7 @@ static void prim_toJSON(EvalState & state, const PosIdx pos, Value ** args, Valu
|
||||
std::ostringstream out;
|
||||
NixStringContext context;
|
||||
printValueAsJSON(state, true, *args[0], pos, out, context);
|
||||
v.mkString(out.view(), context);
|
||||
v.mkString(out.view(), context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_toJSON({
|
||||
@@ -2753,7 +2744,7 @@ bool EvalState::callPathFilter(Value * filterFun, const SourcePath & path, PosId
|
||||
/* Call the filter function. The first argument is the path, the
|
||||
second is a string indicating the type of the file. */
|
||||
Value arg1;
|
||||
arg1.mkString(path.path.abs());
|
||||
arg1.mkString(path.path.abs(), mem);
|
||||
|
||||
// assert that type is not "unknown"
|
||||
Value * args[]{&arg1, const_cast<Value *>(&fileTypeToString(*this, st.type))};
|
||||
@@ -4403,7 +4394,7 @@ static void prim_toString(EvalState & state, const PosIdx pos, Value ** args, Va
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the first argument passed to builtins.toString", true, false);
|
||||
v.mkString(*s, context);
|
||||
v.mkString(*s, context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_toString({
|
||||
@@ -4463,7 +4454,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value ** args, V
|
||||
if (len == 0) {
|
||||
state.forceValue(*args[2], pos);
|
||||
if (args[2]->type() == nString) {
|
||||
v.mkStringNoCopy("", args[2]->context());
|
||||
v.mkStringNoCopy(""_sds, args[2]->context());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -4476,7 +4467,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value ** args, V
|
||||
auto s = state.coerceToString(
|
||||
pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
|
||||
|
||||
v.mkString(NixUInt(start) >= s->size() ? "" : s->substr(start, _len), context);
|
||||
v.mkString(NixUInt(start) >= s->size() ? "" : s->substr(start, _len), context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_substring({
|
||||
@@ -4531,7 +4522,7 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value ** args,
|
||||
auto s =
|
||||
state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.hashString");
|
||||
|
||||
v.mkString(hashString(*ha, s).to_string(HashFormat::Base16, false));
|
||||
v.mkString(hashString(*ha, s).to_string(HashFormat::Base16, false), state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_hashString({
|
||||
@@ -4564,7 +4555,7 @@ static void prim_convertHash(EvalState & state, const PosIdx pos, Value ** args,
|
||||
HashFormat hf = parseHashFormat(
|
||||
state.forceStringNoCtx(*iteratorToHashFormat->value, pos, "while evaluating the attribute 'toHashFormat'"));
|
||||
|
||||
v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI));
|
||||
v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI), state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_convertHash({
|
||||
@@ -4874,7 +4865,7 @@ static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value **
|
||||
"while evaluating one element of the list of strings to concat passed to builtins.concatStringsSep");
|
||||
}
|
||||
|
||||
v.mkString(res, context);
|
||||
v.mkString(res, context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_concatStringsSep({
|
||||
@@ -4949,7 +4940,7 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value ** ar
|
||||
}
|
||||
}
|
||||
|
||||
v.mkString(res, context);
|
||||
v.mkString(res, context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_replaceStrings({
|
||||
@@ -4982,8 +4973,8 @@ static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value ** args
|
||||
state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.parseDrvName");
|
||||
DrvName parsed(name);
|
||||
auto attrs = state.buildBindings(2);
|
||||
attrs.alloc(state.s.name).mkString(parsed.name);
|
||||
attrs.alloc("version").mkString(parsed.version);
|
||||
attrs.alloc(state.s.name).mkString(parsed.name, state.mem);
|
||||
attrs.alloc("version").mkString(parsed.version, state.mem);
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
||||
@@ -5038,7 +5029,7 @@ static void prim_splitVersion(EvalState & state, const PosIdx pos, Value ** args
|
||||
}
|
||||
auto list = state.buildList(components.size());
|
||||
for (const auto & [n, component] : enumerate(components))
|
||||
(list[n] = state.allocValue())->mkString(std::move(component));
|
||||
(list[n] = state.allocValue())->mkString(std::move(component), state.mem);
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
@@ -5182,7 +5173,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
|
||||
});
|
||||
|
||||
if (!settings.pureEval)
|
||||
v.mkString(settings.getCurrentSystem());
|
||||
v.mkString(settings.getCurrentSystem(), mem);
|
||||
addConstant(
|
||||
"__currentSystem",
|
||||
v,
|
||||
@@ -5214,7 +5205,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
|
||||
.impureOnly = true,
|
||||
});
|
||||
|
||||
v.mkString(nixVersion);
|
||||
v.mkString(nixVersion, mem);
|
||||
addConstant(
|
||||
"__nixVersion",
|
||||
v,
|
||||
@@ -5239,7 +5230,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
|
||||
)",
|
||||
});
|
||||
|
||||
v.mkString(store->storeDir);
|
||||
v.mkString(store->storeDir, mem);
|
||||
addConstant(
|
||||
"__storeDir",
|
||||
v,
|
||||
@@ -5304,8 +5295,8 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
|
||||
auto list = buildList(lookupPath.elements.size());
|
||||
for (const auto & [n, i] : enumerate(lookupPath.elements)) {
|
||||
auto attrs = buildBindings(2);
|
||||
attrs.alloc("path").mkString(i.path.s);
|
||||
attrs.alloc("prefix").mkString(i.prefix.s);
|
||||
attrs.alloc("path").mkString(i.path.s, mem);
|
||||
attrs.alloc("prefix").mkString(i.prefix.s, mem);
|
||||
(list[n] = allocValue())->mkAttrs(attrs);
|
||||
}
|
||||
v.mkList(list);
|
||||
|
||||
@@ -11,7 +11,7 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos,
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
|
||||
v.mkString(*s);
|
||||
v.mkString(*s, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_unsafeDiscardStringContext({
|
||||
@@ -69,7 +69,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
|
||||
}
|
||||
}
|
||||
|
||||
v.mkString(*s, context2);
|
||||
v.mkString(*s, context2, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_unsafeDiscardOutputDependency(
|
||||
@@ -137,7 +137,7 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V
|
||||
context.begin()->raw)}),
|
||||
};
|
||||
|
||||
v.mkString(*s, context2);
|
||||
v.mkString(*s, context2, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_addDrvOutputDependencies(
|
||||
@@ -218,7 +218,7 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args,
|
||||
if (!info.second.outputs.empty()) {
|
||||
auto list = state.buildList(info.second.outputs.size());
|
||||
for (const auto & [i, output] : enumerate(info.second.outputs))
|
||||
(list[i] = state.allocValue())->mkString(output);
|
||||
(list[i] = state.allocValue())->mkString(output, state.mem);
|
||||
infoAttrs.alloc(state.s.outputs).mkList(list);
|
||||
}
|
||||
attrs.alloc(state.store->printStorePath(info.first)).mkAttrs(infoAttrs);
|
||||
@@ -321,7 +321,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value ** arg
|
||||
}
|
||||
}
|
||||
|
||||
v.mkString(orig, context);
|
||||
v.mkString(orig, context, state.mem);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_appendContext({.name = "__appendContext", .arity = 2, .fun = prim_appendContext});
|
||||
|
||||
@@ -81,17 +81,17 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** ar
|
||||
attrs.insert_or_assign("rev", rev->gitRev());
|
||||
auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
|
||||
|
||||
auto [storePath, input2] = input.fetchToStore(state.store);
|
||||
auto [storePath, input2] = input.fetchToStore(state.fetchSettings, *state.store);
|
||||
|
||||
auto attrs2 = state.buildBindings(8);
|
||||
state.mkStorePathString(storePath, attrs2.alloc(state.s.outPath));
|
||||
if (input2.getRef())
|
||||
attrs2.alloc("branch").mkString(*input2.getRef());
|
||||
attrs2.alloc("branch").mkString(*input2.getRef(), state.mem);
|
||||
// Backward compatibility: set 'rev' to
|
||||
// 0000000000000000000000000000000000000000 for a dirty tree.
|
||||
auto rev2 = input2.getRev().value_or(Hash(HashAlgorithm::SHA1));
|
||||
attrs2.alloc("rev").mkString(rev2.gitRev());
|
||||
attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12));
|
||||
attrs2.alloc("rev").mkString(rev2.gitRev(), state.mem);
|
||||
attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12), state.mem);
|
||||
if (auto revCount = input2.getRevCount())
|
||||
attrs2.alloc("revCount").mkInt(*revCount);
|
||||
v.mkAttrs(attrs2);
|
||||
|
||||
@@ -35,7 +35,7 @@ void emitTreeAttrs(
|
||||
// FIXME: support arbitrary input attributes.
|
||||
|
||||
if (auto narHash = input.getNarHash())
|
||||
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
|
||||
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true), state.mem);
|
||||
|
||||
if (input.getType() == "git")
|
||||
attrs.alloc("submodules").mkBool(fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
|
||||
@@ -43,13 +43,13 @@ void emitTreeAttrs(
|
||||
if (!forceDirty) {
|
||||
|
||||
if (auto rev = input.getRev()) {
|
||||
attrs.alloc("rev").mkString(rev->gitRev());
|
||||
attrs.alloc("shortRev").mkString(rev->gitShortRev());
|
||||
attrs.alloc("rev").mkString(rev->gitRev(), state.mem);
|
||||
attrs.alloc("shortRev").mkString(rev->gitShortRev(), state.mem);
|
||||
} else if (emptyRevFallback) {
|
||||
// Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
|
||||
auto emptyHash = Hash(HashAlgorithm::SHA1);
|
||||
attrs.alloc("rev").mkString(emptyHash.gitRev());
|
||||
attrs.alloc("shortRev").mkString(emptyHash.gitShortRev());
|
||||
attrs.alloc("rev").mkString(emptyHash.gitRev(), state.mem);
|
||||
attrs.alloc("shortRev").mkString(emptyHash.gitShortRev(), state.mem);
|
||||
}
|
||||
|
||||
if (auto revCount = input.getRevCount())
|
||||
@@ -59,13 +59,14 @@ void emitTreeAttrs(
|
||||
}
|
||||
|
||||
if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) {
|
||||
attrs.alloc("dirtyRev").mkString(*dirtyRev);
|
||||
attrs.alloc("dirtyShortRev").mkString(*fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev"));
|
||||
attrs.alloc("dirtyRev").mkString(*dirtyRev, state.mem);
|
||||
attrs.alloc("dirtyShortRev").mkString(*fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev"), state.mem);
|
||||
}
|
||||
|
||||
if (auto lastModified = input.getLastModified()) {
|
||||
attrs.alloc("lastModified").mkInt(*lastModified);
|
||||
attrs.alloc("lastModifiedDate").mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
|
||||
attrs.alloc("lastModifiedDate")
|
||||
.mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")), state.mem);
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
@@ -82,7 +83,7 @@ struct FetchTreeParams
|
||||
static void fetchTree(
|
||||
EvalState & state, const PosIdx pos, Value ** args, Value & v, const FetchTreeParams & params = FetchTreeParams{})
|
||||
{
|
||||
fetchers::Input input{state.fetchSettings};
|
||||
fetchers::Input input{};
|
||||
NixStringContext context;
|
||||
std::optional<std::string> type;
|
||||
auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree";
|
||||
@@ -194,9 +195,9 @@ static void fetchTree(
|
||||
}
|
||||
|
||||
if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes))
|
||||
input = lookupInRegistries(state.store, input, fetchers::UseRegistries::Limited).first;
|
||||
input = lookupInRegistries(state.fetchSettings, *state.store, input, fetchers::UseRegistries::Limited).first;
|
||||
|
||||
if (state.settings.pureEval && !input.isLocked()) {
|
||||
if (state.settings.pureEval && !input.isLocked(state.fetchSettings)) {
|
||||
if (input.getNarHash())
|
||||
warn(
|
||||
"Input '%s' is unlocked (e.g. lacks a Git revision) but is checked by NAR hash. "
|
||||
@@ -219,7 +220,8 @@ static void fetchTree(
|
||||
throw Error("input '%s' is not allowed to use the '__final' attribute", input.to_string());
|
||||
}
|
||||
|
||||
auto cachedInput = state.inputCache->getAccessor(state.store, input, fetchers::UseRegistries::No);
|
||||
auto cachedInput =
|
||||
state.inputCache->getAccessor(state.fetchSettings, *state.store, input, fetchers::UseRegistries::No);
|
||||
|
||||
auto storePath = state.mountInput(cachedInput.lockedInput, input, cachedInput.accessor);
|
||||
|
||||
@@ -234,229 +236,127 @@ static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, V
|
||||
static RegisterPrimOp primop_fetchTree({
|
||||
.name = "fetchTree",
|
||||
.args = {"input"},
|
||||
.doc = R"(
|
||||
Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with:
|
||||
.doc = []() -> std::string {
|
||||
std::string doc = stripIndentation(R"(
|
||||
Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with:
|
||||
|
||||
- the resulting fixed-output [store path](@docroot@/store/store-path.md)
|
||||
- the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash
|
||||
- backend-specific metadata (currently not documented). <!-- TODO: document output attributes -->
|
||||
- the resulting fixed-output [store path](@docroot@/store/store-path.md)
|
||||
- the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash
|
||||
- backend-specific metadata (currently not documented). <!-- TODO: document output attributes -->
|
||||
|
||||
*input* must be an attribute set with the following attributes:
|
||||
*input* must be an attribute set with the following attributes:
|
||||
|
||||
- `type` (String, required)
|
||||
- `type` (String, required)
|
||||
|
||||
One of the [supported source types](#source-types).
|
||||
This determines other required and allowed input attributes.
|
||||
One of the [supported source types](#source-types).
|
||||
This determines other required and allowed input attributes.
|
||||
|
||||
- `narHash` (String, optional)
|
||||
- `narHash` (String, optional)
|
||||
|
||||
The `narHash` parameter can be used to substitute the source of the tree.
|
||||
It also allows for verification of tree contents that may not be provided by the underlying transfer mechanism.
|
||||
If `narHash` is set, the source is first looked up is the Nix store and [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available.
|
||||
The `narHash` parameter can be used to substitute the source of the tree.
|
||||
It also allows for verification of tree contents that may not be provided by the underlying transfer mechanism.
|
||||
If `narHash` is set, the source is first looked up is the Nix store and [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available.
|
||||
|
||||
A subset of the output attributes of `fetchTree` can be re-used for subsequent calls to `fetchTree` to produce the same result again.
|
||||
That is, `fetchTree` is idempotent.
|
||||
A subset of the output attributes of `fetchTree` can be re-used for subsequent calls to `fetchTree` to produce the same result again.
|
||||
That is, `fetchTree` is idempotent.
|
||||
|
||||
Downloads are cached in `$XDG_CACHE_HOME/nix`.
|
||||
The remote source is fetched from the network if both are true:
|
||||
- A NAR hash is supplied and the corresponding store path is not [valid](@docroot@/glossary.md#gloss-validity), that is, not available in the store
|
||||
Downloads are cached in `$XDG_CACHE_HOME/nix`.
|
||||
The remote source is fetched from the network if both are true:
|
||||
- A NAR hash is supplied and the corresponding store path is not [valid](@docroot@/glossary.md#gloss-validity), that is, not available in the store
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> [Substituters](@docroot@/command-ref/conf-file.md#conf-substituters) are not used in fetching.
|
||||
|
||||
- There is no cache entry or the cache entry is older than [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl)
|
||||
|
||||
## Source types
|
||||
|
||||
The following source types and associated input attributes are supported.
|
||||
|
||||
<!-- TODO: It would be soooo much more predictable to work with (and
|
||||
document) if `fetchTree` was a curried call with the first parameter for
|
||||
`type` or an attribute like `builtins.fetchTree.git`! -->
|
||||
|
||||
- `"file"`
|
||||
|
||||
Place a plain file into the Nix store.
|
||||
This is similar to [`builtins.fetchurl`](@docroot@/language/builtins.md#builtins-fetchurl)
|
||||
|
||||
- `url` (String, required)
|
||||
|
||||
Supported protocols:
|
||||
|
||||
- `https`
|
||||
|
||||
> **Example**
|
||||
> **Note**
|
||||
>
|
||||
> ```nix
|
||||
> fetchTree {
|
||||
> type = "file";
|
||||
> url = "https://example.com/index.html";
|
||||
> }
|
||||
> ```
|
||||
> [Substituters](@docroot@/command-ref/conf-file.md#conf-substituters) are not used in fetching.
|
||||
|
||||
- `http`
|
||||
- There is no cache entry or the cache entry is older than [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl)
|
||||
|
||||
Insecure HTTP transfer for legacy sources.
|
||||
## Source types
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> HTTP performs no encryption or authentication.
|
||||
> Use a `narHash` known in advance to ensure the output has expected contents.
|
||||
The following source types and associated input attributes are supported.
|
||||
|
||||
- `file`
|
||||
<!-- TODO: It would be soooo much more predictable to work with (and
|
||||
document) if `fetchTree` was a curried call with the first parameter for
|
||||
`type` or an attribute like `builtins.fetchTree.git`! -->
|
||||
)");
|
||||
|
||||
A file on the local file system.
|
||||
auto indentString = [](std::string const & str, std::string const & indent) {
|
||||
std::string result;
|
||||
std::istringstream stream(str);
|
||||
std::string line;
|
||||
bool first = true;
|
||||
while (std::getline(stream, line)) {
|
||||
if (!first)
|
||||
result += "\n";
|
||||
result += indent + line;
|
||||
first = false;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```nix
|
||||
> fetchTree {
|
||||
> type = "file";
|
||||
> url = "file:///home/eelco/nix/README.md";
|
||||
> }
|
||||
> ```
|
||||
for (const auto & [schemeName, scheme] : fetchers::getAllInputSchemes()) {
|
||||
doc += "\n- `" + quoteString(schemeName, '"') + "`\n\n";
|
||||
doc += indentString(scheme->schemeDescription(), " ");
|
||||
if (!doc.empty() && doc.back() != '\n')
|
||||
doc += "\n";
|
||||
|
||||
- `"tarball"`
|
||||
for (const auto & [attrName, attribute] : scheme->allowedAttrs()) {
|
||||
doc += "\n - `" + attrName + "` (" + attribute.type + ", "
|
||||
+ (attribute.required ? "required" : "optional") + ")\n\n";
|
||||
doc += indentString(stripIndentation(attribute.doc), " ");
|
||||
if (!doc.empty() && doc.back() != '\n')
|
||||
doc += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
Download a tar archive and extract it into the Nix store.
|
||||
This has the same underlying implementation as [`builtins.fetchTarball`](@docroot@/language/builtins.md#builtins-fetchTarball)
|
||||
doc += "\n" + stripIndentation(R"(
|
||||
The following input types are still subject to change:
|
||||
|
||||
- `url` (String, required)
|
||||
- `"path"`
|
||||
- `"github"`
|
||||
- `"gitlab"`
|
||||
- `"sourcehut"`
|
||||
- `"mercurial"`
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```nix
|
||||
> fetchTree {
|
||||
> type = "tarball";
|
||||
> url = "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-23.11";
|
||||
> }
|
||||
> ```
|
||||
|
||||
- `"git"`
|
||||
|
||||
Fetch a Git tree and copy it to the Nix store.
|
||||
This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit).
|
||||
|
||||
- `url` (String, required)
|
||||
|
||||
The URL formats supported are the same as for Git itself.
|
||||
*input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references).
|
||||
The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> Fetch a GitHub repository using the attribute set representation:
|
||||
>
|
||||
> ```nix
|
||||
> fetchTree {
|
||||
> type = "git";
|
||||
> url = "git@github.com:NixOS/nixpkgs.git";
|
||||
> builtins.fetchTree {
|
||||
> type = "github";
|
||||
> owner = "NixOS";
|
||||
> repo = "nixpkgs";
|
||||
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> This evaluates to the following attribute set:
|
||||
>
|
||||
> ```nix
|
||||
> {
|
||||
> lastModified = 1686503798;
|
||||
> lastModifiedDate = "20230611171638";
|
||||
> narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc=";
|
||||
> outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source";
|
||||
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
|
||||
> shortRev = "ae2e6b3";
|
||||
> }
|
||||
> ```
|
||||
|
||||
> **Note**
|
||||
> **Example**
|
||||
>
|
||||
> If the URL points to a local directory, and no `ref` or `rev` is given, Nix only considers files added to the Git index, as listed by `git ls-files` but use the *current file contents* of the Git working directory.
|
||||
> Fetch the same GitHub repository using the URL-like syntax:
|
||||
>
|
||||
> ```nix
|
||||
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
|
||||
> ```
|
||||
)");
|
||||
|
||||
- `ref` (String, optional)
|
||||
|
||||
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
|
||||
|
||||
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
|
||||
|
||||
Default: `"HEAD"`
|
||||
|
||||
- `rev` (String, optional)
|
||||
|
||||
A Git revision; a commit hash.
|
||||
|
||||
Default: the tip of `ref`
|
||||
|
||||
- `shallow` (Bool, optional)
|
||||
|
||||
Make a shallow clone when fetching the Git tree.
|
||||
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
|
||||
|
||||
Default: `true`
|
||||
|
||||
- `submodules` (Bool, optional)
|
||||
|
||||
Also fetch submodules if available.
|
||||
|
||||
Default: `false`
|
||||
|
||||
- `lfs` (Bool, optional)
|
||||
|
||||
Fetch any [Git LFS](https://git-lfs.com/) files.
|
||||
|
||||
Default: `false`
|
||||
|
||||
- `allRefs` (Bool, optional)
|
||||
|
||||
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
|
||||
|
||||
Whether to fetch all references (eg. branches and tags) of the repository.
|
||||
With this argument being true, it's possible to load a `rev` from *any* `ref`.
|
||||
(Without setting this option, only `rev`s from the specified `ref` are supported).
|
||||
|
||||
Default: `false`
|
||||
|
||||
- `lastModified` (Integer, optional)
|
||||
|
||||
Unix timestamp of the fetched commit.
|
||||
|
||||
If set, pass through the value to the output attribute set.
|
||||
Otherwise, generated from the fetched Git tree.
|
||||
|
||||
- `revCount` (Integer, optional)
|
||||
|
||||
Number of revisions in the history of the Git repository before the fetched commit.
|
||||
|
||||
If set, pass through the value to the output attribute set.
|
||||
Otherwise, generated from the fetched Git tree.
|
||||
|
||||
The following input types are still subject to change:
|
||||
|
||||
- `"path"`
|
||||
- `"github"`
|
||||
- `"gitlab"`
|
||||
- `"sourcehut"`
|
||||
- `"mercurial"`
|
||||
|
||||
*input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references).
|
||||
The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> Fetch a GitHub repository using the attribute set representation:
|
||||
>
|
||||
> ```nix
|
||||
> builtins.fetchTree {
|
||||
> type = "github";
|
||||
> owner = "NixOS";
|
||||
> repo = "nixpkgs";
|
||||
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> This evaluates to the following attribute set:
|
||||
>
|
||||
> ```nix
|
||||
> {
|
||||
> lastModified = 1686503798;
|
||||
> lastModifiedDate = "20230611171638";
|
||||
> narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc=";
|
||||
> outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source";
|
||||
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
|
||||
> shortRev = "ae2e6b3";
|
||||
> }
|
||||
> ```
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> Fetch the same GitHub repository using the URL-like syntax:
|
||||
>
|
||||
> ```nix
|
||||
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
|
||||
> ```
|
||||
)",
|
||||
return doc;
|
||||
}(),
|
||||
.fun = prim_fetchTree,
|
||||
.experimentalFeature = Xp::FetchTree,
|
||||
});
|
||||
@@ -581,10 +481,10 @@ static void fetch(
|
||||
auto storePath = unpack ? fetchToStore(
|
||||
state.fetchSettings,
|
||||
*state.store,
|
||||
fetchers::downloadTarball(state.store, state.fetchSettings, *url),
|
||||
fetchers::downloadTarball(*state.store, state.fetchSettings, *url),
|
||||
FetchMode::Copy,
|
||||
name)
|
||||
: fetchers::downloadFile(state.store, state.fetchSettings, *url, name).storePath;
|
||||
: fetchers::downloadFile(*state.store, state.fetchSettings, *url, name).storePath;
|
||||
|
||||
if (expectedHash) {
|
||||
auto hash = unpack ? state.store->queryPathInfo(storePath)->narHash
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "nix/expr/primops.hh"
|
||||
#include "nix/expr/eval-inline.hh"
|
||||
#include "nix/expr/static-string-data.hh"
|
||||
|
||||
#include "expr-config-private.hh"
|
||||
|
||||
@@ -125,7 +126,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
|
||||
case toml::value_t::string: {
|
||||
auto s = toml::get<std::string_view>(t);
|
||||
forceNoNullByte(s);
|
||||
v.mkString(s);
|
||||
v.mkString(s, state.mem);
|
||||
} break;
|
||||
case toml::value_t::local_datetime:
|
||||
case toml::value_t::offset_datetime:
|
||||
@@ -136,12 +137,12 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
|
||||
normalizeDatetimeFormat(t);
|
||||
#endif
|
||||
auto attrs = state.buildBindings(2);
|
||||
attrs.alloc("_type").mkStringNoCopy("timestamp");
|
||||
attrs.alloc("_type").mkStringNoCopy("timestamp"_sds);
|
||||
std::ostringstream s;
|
||||
s << t;
|
||||
auto str = s.view();
|
||||
forceNoNullByte(str);
|
||||
attrs.alloc("value").mkString(str);
|
||||
attrs.alloc("value").mkString(str, state.mem);
|
||||
v.mkAttrs(attrs);
|
||||
} else {
|
||||
throw std::runtime_error("Dates and times are not supported");
|
||||
|
||||
@@ -16,6 +16,8 @@ json printValueAsJSON(
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
auto _level = state.addCallDepth(pos);
|
||||
|
||||
if (strict)
|
||||
state.forceValue(v, pos);
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#include "nix/fetchers/git-utils.hh"
|
||||
#include "nix/util/file-system.hh"
|
||||
#include <gmock/gmock.h>
|
||||
#include <git2-experimental/global.h>
|
||||
#include <git2-experimental/repository.h>
|
||||
#include <git2-experimental/signature.h>
|
||||
#include <git2-experimental/types.h>
|
||||
#include <git2-experimental/object.h>
|
||||
#include <git2-experimental/tag.h>
|
||||
#include <git2/global.h>
|
||||
#include <git2/repository.h>
|
||||
#include <git2/signature.h>
|
||||
#include <git2/types.h>
|
||||
#include <git2/object.h>
|
||||
#include <git2/tag.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "nix/util/fs-sink.hh"
|
||||
#include "nix/util/serialise.hh"
|
||||
#include "nix/fetchers/git-lfs-fetch.hh"
|
||||
|
||||
#include <git2-experimental/blob.h>
|
||||
#include <git2-experimental/tree.h>
|
||||
#include <git2/blob.h>
|
||||
#include <git2/tree.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -92,7 +92,7 @@ TEST_F(GitUtilsTest, sink_basic)
|
||||
// sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello"));
|
||||
|
||||
auto result = repo->dereferenceSingletonDirectory(sink->flush());
|
||||
auto accessor = repo->getAccessor(result, false, getRepoName());
|
||||
auto accessor = repo->getAccessor(result, {}, getRepoName());
|
||||
auto entries = accessor->readDirectory(CanonPath::root);
|
||||
ASSERT_EQ(entries.size(), 5u);
|
||||
ASSERT_EQ(accessor->readFile(CanonPath("hello")), "hello world");
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "nix/fetchers/fetchers.hh"
|
||||
#include "nix/fetchers/git-utils.hh"
|
||||
|
||||
#include <git2-experimental.h>
|
||||
#include <git2.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
@@ -196,7 +196,7 @@ TEST_F(GitTest, submodulePeriodSupport)
|
||||
{"ref", "main"},
|
||||
});
|
||||
|
||||
auto [accessor, i] = input.getAccessor(store);
|
||||
auto [accessor, i] = input.getAccessor(settings, *store);
|
||||
|
||||
ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n");
|
||||
}
|
||||
|
||||
61
src/libfetchers-tests/input.cc
Normal file
61
src/libfetchers-tests/input.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "nix/fetchers/fetch-settings.hh"
|
||||
#include "nix/fetchers/attrs.hh"
|
||||
#include "nix/fetchers/fetchers.hh"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace nix {
|
||||
|
||||
using fetchers::Attr;
|
||||
|
||||
struct InputFromAttrsTestCase
|
||||
{
|
||||
fetchers::Attrs attrs;
|
||||
std::string expectedUrl;
|
||||
std::string description;
|
||||
fetchers::Attrs expectedAttrs = attrs;
|
||||
};
|
||||
|
||||
class InputFromAttrsTest : public ::testing::WithParamInterface<InputFromAttrsTestCase>, public ::testing::Test
|
||||
{};
|
||||
|
||||
TEST_P(InputFromAttrsTest, attrsAreCorrectAndRoundTrips)
|
||||
{
|
||||
fetchers::Settings fetchSettings;
|
||||
|
||||
const auto & testCase = GetParam();
|
||||
|
||||
auto input = fetchers::Input::fromAttrs(fetchSettings, fetchers::Attrs(testCase.attrs));
|
||||
|
||||
EXPECT_EQ(input.toAttrs(), testCase.expectedAttrs);
|
||||
EXPECT_EQ(input.toURLString(), testCase.expectedUrl);
|
||||
|
||||
auto input2 = fetchers::Input::fromAttrs(fetchSettings, input.toAttrs());
|
||||
EXPECT_EQ(input, input2);
|
||||
EXPECT_EQ(input.toAttrs(), input2.toAttrs());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
InputFromAttrs,
|
||||
InputFromAttrsTest,
|
||||
::testing::Values(
|
||||
// Test for issue #14429.
|
||||
InputFromAttrsTestCase{
|
||||
.attrs =
|
||||
{
|
||||
{"url", Attr("git+ssh://git@github.com/NixOS/nixpkgs")},
|
||||
{"type", Attr("git")},
|
||||
},
|
||||
.expectedUrl = "git+ssh://git@github.com/NixOS/nixpkgs",
|
||||
.description = "strips_git_plus_prefix",
|
||||
.expectedAttrs =
|
||||
{
|
||||
{"url", Attr("ssh://git@github.com/NixOS/nixpkgs")},
|
||||
{"type", Attr("git")},
|
||||
},
|
||||
}),
|
||||
[](const ::testing::TestParamInfo<InputFromAttrsTestCase> & info) { return info.param.description; });
|
||||
|
||||
} // namespace nix
|
||||
@@ -33,7 +33,7 @@ deps_private += rapidcheck
|
||||
gtest = dependency('gtest', main : true)
|
||||
deps_private += gtest
|
||||
|
||||
libgit2 = dependency('libgit2-experimental')
|
||||
libgit2 = dependency('libgit2')
|
||||
deps_private += libgit2
|
||||
|
||||
subdir('nix-meson-build-support/common')
|
||||
@@ -42,6 +42,7 @@ sources = files(
|
||||
'access-tokens.cc',
|
||||
'git-utils.cc',
|
||||
'git.cc',
|
||||
'input.cc',
|
||||
'nix_api_fetchers.cc',
|
||||
'public-key.cc',
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "nix/fetchers/attrs.hh"
|
||||
#include "nix/fetchers/fetchers.hh"
|
||||
#include "nix/fetchers/git-utils.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -112,7 +111,7 @@ StringMap attrsToQuery(const Attrs & attrs)
|
||||
|
||||
Hash getRevAttr(const Attrs & attrs, const std::string & name)
|
||||
{
|
||||
return parseGitHash(getStrAttr(attrs, name));
|
||||
return Hash::parseAny(getStrAttr(attrs, name), HashAlgorithm::SHA1);
|
||||
}
|
||||
|
||||
} // namespace nix::fetchers
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "nix/fetchers/fetch-settings.hh"
|
||||
#include "nix/fetchers/fetch-to-store.hh"
|
||||
#include "nix/util/url.hh"
|
||||
#include "nix/util/archive.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -26,18 +27,9 @@ void registerInputScheme(std::shared_ptr<InputScheme> && inputScheme)
|
||||
throw Error("Input scheme with name %s already registered", schemeName);
|
||||
}
|
||||
|
||||
nlohmann::json dumpRegisterInputSchemeInfo()
|
||||
const InputSchemeMap & getAllInputSchemes()
|
||||
{
|
||||
using nlohmann::json;
|
||||
|
||||
auto res = json::object();
|
||||
|
||||
for (auto & [name, scheme] : inputSchemes()) {
|
||||
auto & r = res[name] = json::object();
|
||||
r["allowedAttrs"] = scheme->allowedAttrs();
|
||||
}
|
||||
|
||||
return res;
|
||||
return inputSchemes();
|
||||
}
|
||||
|
||||
Input Input::fromURL(const Settings & settings, const std::string & url, bool requireTree)
|
||||
@@ -89,7 +81,7 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
|
||||
// but not all of them. Doing this is to support those other
|
||||
// operations which are supposed to be robust on
|
||||
// unknown/uninterpretable inputs.
|
||||
Input input{settings};
|
||||
Input input;
|
||||
input.attrs = attrs;
|
||||
fixupInput(input);
|
||||
return input;
|
||||
@@ -119,7 +111,7 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
|
||||
return std::move(*res);
|
||||
}
|
||||
|
||||
std::optional<std::string> Input::getFingerprint(ref<Store> store) const
|
||||
std::optional<std::string> Input::getFingerprint(Store & store) const
|
||||
{
|
||||
if (!scheme)
|
||||
return std::nullopt;
|
||||
@@ -159,9 +151,9 @@ bool Input::isDirect() const
|
||||
return !scheme || scheme->isDirect(*this);
|
||||
}
|
||||
|
||||
bool Input::isLocked() const
|
||||
bool Input::isLocked(const Settings & settings) const
|
||||
{
|
||||
return scheme && scheme->isLocked(*this);
|
||||
return scheme && scheme->isLocked(settings, *this);
|
||||
}
|
||||
|
||||
bool Input::isFinal() const
|
||||
@@ -198,19 +190,19 @@ bool Input::contains(const Input & other) const
|
||||
}
|
||||
|
||||
// FIXME: remove
|
||||
std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
|
||||
std::pair<StorePath, Input> Input::fetchToStore(const Settings & settings, Store & store) const
|
||||
{
|
||||
if (!scheme)
|
||||
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
|
||||
|
||||
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
|
||||
try {
|
||||
auto [accessor, result] = getAccessorUnchecked(store);
|
||||
auto [accessor, result] = getAccessorUnchecked(settings, store);
|
||||
|
||||
auto storePath =
|
||||
nix::fetchToStore(*settings, *store, SourcePath(accessor), FetchMode::Copy, result.getName());
|
||||
nix::fetchToStore(settings, store, SourcePath(accessor), FetchMode::Copy, result.getName());
|
||||
|
||||
auto narHash = store->queryPathInfo(storePath)->narHash;
|
||||
auto narHash = store.queryPathInfo(storePath)->narHash;
|
||||
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
|
||||
|
||||
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
|
||||
@@ -297,10 +289,10 @@ void Input::checkLocks(Input specified, Input & result)
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<ref<SourceAccessor>, Input> Input::getAccessor(ref<Store> store) const
|
||||
std::pair<ref<SourceAccessor>, Input> Input::getAccessor(const Settings & settings, Store & store) const
|
||||
{
|
||||
try {
|
||||
auto [accessor, result] = getAccessorUnchecked(store);
|
||||
auto [accessor, result] = getAccessorUnchecked(settings, store);
|
||||
|
||||
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
|
||||
|
||||
@@ -313,7 +305,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessor(ref<Store> store) const
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> store) const
|
||||
std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings & settings, Store & store) const
|
||||
{
|
||||
// FIXME: cache the accessor
|
||||
|
||||
@@ -333,13 +325,13 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
|
||||
*/
|
||||
if (isFinal() && getNarHash()) {
|
||||
try {
|
||||
auto storePath = computeStorePath(*store);
|
||||
auto storePath = computeStorePath(store);
|
||||
|
||||
store->ensurePath(storePath);
|
||||
store.ensurePath(storePath);
|
||||
|
||||
debug("using substituted/cached input '%s' in '%s'", to_string(), store->printStorePath(storePath));
|
||||
debug("using substituted/cached input '%s' in '%s'", to_string(), store.printStorePath(storePath));
|
||||
|
||||
auto accessor = store->requireStoreObjectAccessor(storePath);
|
||||
auto accessor = store.requireStoreObjectAccessor(storePath);
|
||||
|
||||
accessor->fingerprint = getFingerprint(store);
|
||||
|
||||
@@ -349,7 +341,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
|
||||
if (accessor->fingerprint) {
|
||||
ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive;
|
||||
auto cacheKey = makeFetchToStoreCacheKey(getName(), *accessor->fingerprint, method, "/");
|
||||
settings->getCache()->upsert(cacheKey, *store, {}, storePath);
|
||||
settings.getCache()->upsert(cacheKey, store, {}, storePath);
|
||||
}
|
||||
|
||||
accessor->setPathDisplay("«" + to_string() + "»");
|
||||
@@ -360,7 +352,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
|
||||
}
|
||||
}
|
||||
|
||||
auto [accessor, result] = scheme->getAccessor(store, *this);
|
||||
auto [accessor, result] = scheme->getAccessor(settings, store, *this);
|
||||
|
||||
if (!accessor->fingerprint)
|
||||
accessor->fingerprint = result.getFingerprint(store);
|
||||
@@ -377,10 +369,10 @@ Input Input::applyOverrides(std::optional<std::string> ref, std::optional<Hash>
|
||||
return scheme->applyOverrides(*this, ref, rev);
|
||||
}
|
||||
|
||||
void Input::clone(const Path & destDir) const
|
||||
void Input::clone(const Settings & settings, Store & store, const std::filesystem::path & destDir) const
|
||||
{
|
||||
assert(scheme);
|
||||
scheme->clone(*this, destDir);
|
||||
scheme->clone(settings, store, *this, destDir);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> Input::getSourcePath() const
|
||||
@@ -447,9 +439,6 @@ std::optional<Hash> Input::getRev() const
|
||||
} catch (BadHash & e) {
|
||||
// Default to sha1 for backwards compatibility with existing
|
||||
// usages (e.g. `builtins.fetchTree` calls or flake inputs).
|
||||
//
|
||||
// Note that means that for SHA-256 git repos, prefixing
|
||||
// must be used.
|
||||
hash = Hash::parseAny(*s, HashAlgorithm::SHA1);
|
||||
}
|
||||
}
|
||||
@@ -496,9 +485,19 @@ void InputScheme::putFile(
|
||||
throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path);
|
||||
}
|
||||
|
||||
void InputScheme::clone(const Input & input, const Path & destDir) const
|
||||
void InputScheme::clone(
|
||||
const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir) const
|
||||
{
|
||||
throw Error("do not know how to clone input '%s'", input.to_string());
|
||||
if (std::filesystem::exists(destDir))
|
||||
throw Error("cannot clone into existing path %s", destDir);
|
||||
|
||||
auto [accessor, input2] = getAccessor(settings, store, input);
|
||||
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s' to %s...", input2.to_string(), destDir));
|
||||
|
||||
RestoreSink sink(/*startFsync=*/false);
|
||||
sink.dstPath = destDir;
|
||||
copyRecursive(*accessor, CanonPath::root, sink, CanonPath::root);
|
||||
}
|
||||
|
||||
std::optional<ExperimentalFeature> InputScheme::experimentalFeature() const
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#include "nix/util/hash.hh"
|
||||
#include "nix/store/ssh.hh"
|
||||
|
||||
#include <git2-experimental/attr.h>
|
||||
#include <git2-experimental/config.h>
|
||||
#include <git2-experimental/errors.h>
|
||||
#include <git2-experimental/remote.h>
|
||||
#include <git2/attr.h>
|
||||
#include <git2/config.h>
|
||||
#include <git2/errors.h>
|
||||
#include <git2/remote.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -209,7 +209,7 @@ std::vector<nlohmann::json> Fetch::fetchUrls(const std::vector<Pointer> & pointe
|
||||
auto url = api.endpoint + "/objects/batch";
|
||||
const auto & authHeader = api.authHeader;
|
||||
FileTransferRequest request(parseURL(url));
|
||||
request.method = HttpMethod::POST;
|
||||
request.method = HttpMethod::Post;
|
||||
Headers headers;
|
||||
if (authHeader.has_value())
|
||||
headers.push_back({"Authorization", *authHeader});
|
||||
|
||||
@@ -13,27 +13,27 @@
|
||||
#include "nix/util/thread-pool.hh"
|
||||
#include "nix/util/pool.hh"
|
||||
|
||||
#include <git2-experimental/attr.h>
|
||||
#include <git2-experimental/blob.h>
|
||||
#include <git2-experimental/branch.h>
|
||||
#include <git2-experimental/commit.h>
|
||||
#include <git2-experimental/config.h>
|
||||
#include <git2-experimental/describe.h>
|
||||
#include <git2-experimental/errors.h>
|
||||
#include <git2-experimental/global.h>
|
||||
#include <git2-experimental/indexer.h>
|
||||
#include <git2-experimental/object.h>
|
||||
#include <git2-experimental/odb.h>
|
||||
#include <git2-experimental/refs.h>
|
||||
#include <git2-experimental/remote.h>
|
||||
#include <git2-experimental/repository.h>
|
||||
#include <git2-experimental/revparse.h>
|
||||
#include <git2-experimental/status.h>
|
||||
#include <git2-experimental/submodule.h>
|
||||
#include <git2-experimental/sys/odb_backend.h>
|
||||
#include <git2-experimental/sys/mempack.h>
|
||||
#include <git2-experimental/tag.h>
|
||||
#include <git2-experimental/tree.h>
|
||||
#include <git2/attr.h>
|
||||
#include <git2/blob.h>
|
||||
#include <git2/branch.h>
|
||||
#include <git2/commit.h>
|
||||
#include <git2/config.h>
|
||||
#include <git2/describe.h>
|
||||
#include <git2/errors.h>
|
||||
#include <git2/global.h>
|
||||
#include <git2/indexer.h>
|
||||
#include <git2/object.h>
|
||||
#include <git2/odb.h>
|
||||
#include <git2/refs.h>
|
||||
#include <git2/remote.h>
|
||||
#include <git2/repository.h>
|
||||
#include <git2/revparse.h>
|
||||
#include <git2/status.h>
|
||||
#include <git2/submodule.h>
|
||||
#include <git2/sys/odb_backend.h>
|
||||
#include <git2/sys/mempack.h>
|
||||
#include <git2/tag.h>
|
||||
#include <git2/tree.h>
|
||||
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
@@ -91,21 +91,10 @@ typedef std::unique_ptr<git_indexer, Deleter<git_indexer_free>> Indexer;
|
||||
|
||||
Hash toHash(const git_oid & oid)
|
||||
{
|
||||
HashAlgorithm algo;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (oid.type) {
|
||||
case GIT_OID_SHA1:
|
||||
algo = HashAlgorithm::SHA1;
|
||||
break;
|
||||
case GIT_OID_SHA256:
|
||||
algo = HashAlgorithm::SHA256;
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
Hash hash(algo);
|
||||
#ifdef GIT_EXPERIMENTAL_SHA256
|
||||
assert(oid.type == GIT_OID_SHA1);
|
||||
#endif
|
||||
Hash hash(HashAlgorithm::SHA1);
|
||||
memcpy(hash.hash, oid.id, hash.hashSize);
|
||||
return hash;
|
||||
}
|
||||
@@ -122,21 +111,7 @@ static void initLibGit2()
|
||||
git_oid hashToOID(const Hash & hash)
|
||||
{
|
||||
git_oid oid;
|
||||
git_oid_t t;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (hash.algo) {
|
||||
case HashAlgorithm::SHA1:
|
||||
t = GIT_OID_SHA1;
|
||||
break;
|
||||
case HashAlgorithm::SHA256:
|
||||
t = GIT_OID_SHA256;
|
||||
break;
|
||||
default:
|
||||
throw Error("unsupported hash algorithm for Git: %s", printHashAlgo(hash.algo));
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
if (git_oid_fromstr(&oid, hash.gitRev().c_str(), t))
|
||||
if (git_oid_fromstr(&oid, hash.gitRev().c_str()))
|
||||
throw Error("cannot convert '%s' to a Git OID", hash.gitRev());
|
||||
return oid;
|
||||
}
|
||||
@@ -329,8 +304,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||
// (synchronously on the git_packbuilder_write_buf thread)
|
||||
Indexer indexer;
|
||||
git_indexer_progress stats;
|
||||
git_indexer_options indexer_opts = GIT_INDEXER_OPTIONS_INIT;
|
||||
if (git_indexer_new(Setter(indexer), pack_dir_path.c_str(), &indexer_opts))
|
||||
if (git_indexer_new(Setter(indexer), pack_dir_path.c_str(), 0, nullptr, nullptr))
|
||||
throw Error("creating git packfile indexer: %s", git_error_last()->message);
|
||||
|
||||
// TODO: provide index callback for checkInterrupt() termination
|
||||
@@ -567,14 +541,15 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||
}
|
||||
|
||||
/**
|
||||
* A 'GitSourceAccessor' with no regard for export-ignore or any other transformations.
|
||||
* A 'GitSourceAccessor' with no regard for export-ignore.
|
||||
*/
|
||||
ref<GitSourceAccessor> getRawAccessor(const Hash & rev, bool smudgeLfs = false);
|
||||
ref<GitSourceAccessor> getRawAccessor(const Hash & rev, const GitAccessorOptions & options);
|
||||
|
||||
ref<SourceAccessor>
|
||||
getAccessor(const Hash & rev, bool exportIgnore, std::string displayPrefix, bool smudgeLfs = false) override;
|
||||
getAccessor(const Hash & rev, const GitAccessorOptions & options, std::string displayPrefix) override;
|
||||
|
||||
ref<SourceAccessor> getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError e) override;
|
||||
ref<SourceAccessor>
|
||||
getAccessor(const WorkdirInfo & wd, const GitAccessorOptions & options, MakeNotAllowedError e) override;
|
||||
|
||||
ref<GitFileSystemObjectSink> getFileSystemObjectSink() override;
|
||||
|
||||
@@ -694,7 +669,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||
|
||||
Hash treeHashToNarHash(const fetchers::Settings & settings, const Hash & treeHash) override
|
||||
{
|
||||
auto accessor = getAccessor(treeHash, false, "");
|
||||
auto accessor = getAccessor(treeHash, {}, "");
|
||||
|
||||
fetchers::Cache::Key cacheKey{"treeHashToNarHash", {{"treeHash", treeHash.gitRev()}}};
|
||||
|
||||
@@ -742,15 +717,17 @@ struct GitSourceAccessor : SourceAccessor
|
||||
ref<GitRepoImpl> repo;
|
||||
Object root;
|
||||
std::optional<lfs::Fetch> lfsFetch = std::nullopt;
|
||||
GitAccessorOptions options;
|
||||
};
|
||||
|
||||
Sync<State> state_;
|
||||
|
||||
GitSourceAccessor(ref<GitRepoImpl> repo_, const Hash & rev, bool smudgeLfs)
|
||||
GitSourceAccessor(ref<GitRepoImpl> repo_, const Hash & rev, const GitAccessorOptions & options)
|
||||
: state_{State{
|
||||
.repo = repo_,
|
||||
.root = peelToTreeOrBlob(lookupObject(*repo_, hashToOID(rev)).get()),
|
||||
.lfsFetch = smudgeLfs ? std::make_optional(lfs::Fetch(*repo_, hashToOID(rev))) : std::nullopt,
|
||||
.lfsFetch = options.smudgeLfs ? std::make_optional(lfs::Fetch(*repo_, hashToOID(rev))) : std::nullopt,
|
||||
.options = options,
|
||||
}}
|
||||
{
|
||||
}
|
||||
@@ -1280,26 +1257,26 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
|
||||
}
|
||||
};
|
||||
|
||||
ref<GitSourceAccessor> GitRepoImpl::getRawAccessor(const Hash & rev, bool smudgeLfs)
|
||||
ref<GitSourceAccessor> GitRepoImpl::getRawAccessor(const Hash & rev, const GitAccessorOptions & options)
|
||||
{
|
||||
auto self = ref<GitRepoImpl>(shared_from_this());
|
||||
return make_ref<GitSourceAccessor>(self, rev, smudgeLfs);
|
||||
return make_ref<GitSourceAccessor>(self, rev, options);
|
||||
}
|
||||
|
||||
ref<SourceAccessor>
|
||||
GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore, std::string displayPrefix, bool smudgeLfs)
|
||||
GitRepoImpl::getAccessor(const Hash & rev, const GitAccessorOptions & options, std::string displayPrefix)
|
||||
{
|
||||
auto self = ref<GitRepoImpl>(shared_from_this());
|
||||
ref<GitSourceAccessor> rawGitAccessor = getRawAccessor(rev, smudgeLfs);
|
||||
ref<GitSourceAccessor> rawGitAccessor = getRawAccessor(rev, options);
|
||||
rawGitAccessor->setPathDisplay(std::move(displayPrefix));
|
||||
if (exportIgnore)
|
||||
if (options.exportIgnore)
|
||||
return make_ref<GitExportIgnoreSourceAccessor>(self, rawGitAccessor, rev);
|
||||
else
|
||||
return rawGitAccessor;
|
||||
}
|
||||
|
||||
ref<SourceAccessor>
|
||||
GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError)
|
||||
ref<SourceAccessor> GitRepoImpl::getAccessor(
|
||||
const WorkdirInfo & wd, const GitAccessorOptions & options, MakeNotAllowedError makeNotAllowedError)
|
||||
{
|
||||
auto self = ref<GitRepoImpl>(shared_from_this());
|
||||
ref<SourceAccessor> fileAccessor = AllowListSourceAccessor::create(
|
||||
@@ -1309,10 +1286,9 @@ GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllow
|
||||
boost::unordered_flat_set<CanonPath>{CanonPath::root},
|
||||
std::move(makeNotAllowedError))
|
||||
.cast<SourceAccessor>();
|
||||
if (exportIgnore)
|
||||
return make_ref<GitExportIgnoreSourceAccessor>(self, fileAccessor, std::nullopt);
|
||||
else
|
||||
return fileAccessor;
|
||||
if (options.exportIgnore)
|
||||
fileAccessor = make_ref<GitExportIgnoreSourceAccessor>(self, fileAccessor, std::nullopt);
|
||||
return fileAccessor;
|
||||
}
|
||||
|
||||
ref<GitFileSystemObjectSink> GitRepoImpl::getFileSystemObjectSink()
|
||||
@@ -1325,7 +1301,7 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
|
||||
/* Read the .gitmodules files from this revision. */
|
||||
CanonPath modulesFile(".gitmodules");
|
||||
|
||||
auto accessor = getAccessor(rev, exportIgnore, "");
|
||||
auto accessor = getAccessor(rev, {.exportIgnore = exportIgnore}, "");
|
||||
if (!accessor->pathExists(modulesFile))
|
||||
return {};
|
||||
|
||||
@@ -1342,7 +1318,7 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
|
||||
|
||||
std::vector<std::tuple<Submodule, Hash>> result;
|
||||
|
||||
auto rawAccessor = getRawAccessor(rev);
|
||||
auto rawAccessor = getRawAccessor(rev, {});
|
||||
|
||||
for (auto & submodule : parseSubmodules(pathTemp)) {
|
||||
/* Filter out .gitmodules entries that don't exist or are not
|
||||
@@ -1354,13 +1330,16 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
|
||||
return result;
|
||||
}
|
||||
|
||||
ref<GitRepo> getTarballCache()
|
||||
namespace fetchers {
|
||||
|
||||
ref<GitRepo> Settings::getTarballCache() const
|
||||
{
|
||||
static auto repoDir = std::filesystem::path(getCacheDir()) / "tarball-cache";
|
||||
|
||||
return GitRepo::openRepo(repoDir, true, true);
|
||||
}
|
||||
|
||||
} // namespace fetchers
|
||||
|
||||
GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path & path)
|
||||
{
|
||||
static Sync<std::map<std::filesystem::path, WorkdirInfo>> _cache;
|
||||
@@ -1404,21 +1383,4 @@ bool isLegalRefName(const std::string & refName)
|
||||
return false;
|
||||
}
|
||||
|
||||
Hash parseGitHash(std::string_view hashStr)
|
||||
{
|
||||
HashAlgorithm algo;
|
||||
switch (hashStr.size()) {
|
||||
case 40:
|
||||
algo = HashAlgorithm::SHA1;
|
||||
break;
|
||||
case 64:
|
||||
algo = HashAlgorithm::SHA256;
|
||||
break;
|
||||
default:
|
||||
throw Error(
|
||||
"invalid git hash '%s': expected 40 (SHA1) or 64 (SHA256) hex characters, got %d", hashStr, hashStr.size());
|
||||
}
|
||||
return Hash::parseNonSRIUnprefixed(hashStr, algo);
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -168,8 +168,6 @@ struct GitInputScheme : InputScheme
|
||||
return {};
|
||||
|
||||
auto url2(url);
|
||||
if (hasPrefix(url2.scheme, "git+"))
|
||||
url2.scheme = std::string(url2.scheme, 4);
|
||||
url2.query.clear();
|
||||
|
||||
Attrs attrs;
|
||||
@@ -196,28 +194,183 @@ struct GitInputScheme : InputScheme
|
||||
return "git";
|
||||
}
|
||||
|
||||
StringSet allowedAttrs() const override
|
||||
std::string schemeDescription() const override
|
||||
{
|
||||
return {
|
||||
"url",
|
||||
"ref",
|
||||
"rev",
|
||||
"shallow",
|
||||
"submodules",
|
||||
"lfs",
|
||||
"exportIgnore",
|
||||
"lastModified",
|
||||
"revCount",
|
||||
"narHash",
|
||||
"allRefs",
|
||||
"name",
|
||||
"dirtyRev",
|
||||
"dirtyShortRev",
|
||||
"verifyCommit",
|
||||
"keytype",
|
||||
"publicKey",
|
||||
"publicKeys",
|
||||
return stripIndentation(R"(
|
||||
Fetch a Git tree and copy it to the Nix store.
|
||||
This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit).
|
||||
)");
|
||||
}
|
||||
|
||||
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
|
||||
{
|
||||
static const std::map<std::string, AttributeInfo> attrs = {
|
||||
{
|
||||
"url",
|
||||
{
|
||||
.type = "String",
|
||||
.required = true,
|
||||
.doc = R"(
|
||||
The URL formats supported are the same as for Git itself.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```nix
|
||||
> fetchTree {
|
||||
> type = "git";
|
||||
> url = "git@github.com:NixOS/nixpkgs.git";
|
||||
> }
|
||||
> ```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> If the URL points to a local directory, and no `ref` or `rev` is given, Nix only considers files added to the Git index, as listed by `git ls-files` but uses the *current file contents* of the Git working directory.
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"ref",
|
||||
{
|
||||
.type = "String",
|
||||
.required = false,
|
||||
.doc = R"(
|
||||
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
|
||||
|
||||
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
|
||||
|
||||
Default: `"HEAD"`
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"rev",
|
||||
{
|
||||
.type = "String",
|
||||
.required = false,
|
||||
.doc = R"(
|
||||
A Git revision; a commit hash.
|
||||
|
||||
Default: the tip of `ref`
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"shallow",
|
||||
{
|
||||
.type = "Bool",
|
||||
.required = false,
|
||||
.doc = R"(
|
||||
Make a shallow clone when fetching the Git tree.
|
||||
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
|
||||
|
||||
Default: `true`
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"submodules",
|
||||
{
|
||||
.type = "Bool",
|
||||
.required = false,
|
||||
.doc = R"(
|
||||
Also fetch submodules if available.
|
||||
|
||||
Default: `false`
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"lfs",
|
||||
{
|
||||
.type = "Bool",
|
||||
.required = false,
|
||||
.doc = R"(
|
||||
Fetch any [Git LFS](https://git-lfs.com/) files.
|
||||
|
||||
Default: `false`
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"exportIgnore",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"lastModified",
|
||||
{
|
||||
.type = "Integer",
|
||||
.required = false,
|
||||
.doc = R"(
|
||||
Unix timestamp of the fetched commit.
|
||||
|
||||
If set, pass through the value to the output attribute set.
|
||||
Otherwise, generated from the fetched Git tree.
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"revCount",
|
||||
{
|
||||
.type = "Integer",
|
||||
.required = false,
|
||||
.doc = R"(
|
||||
Number of revisions in the history of the Git repository before the fetched commit.
|
||||
|
||||
If set, pass through the value to the output attribute set.
|
||||
Otherwise, generated from the fetched Git tree.
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"narHash",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"allRefs",
|
||||
{
|
||||
.type = "Bool",
|
||||
.required = false,
|
||||
.doc = R"(
|
||||
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
|
||||
|
||||
Whether to fetch all references (eg. branches and tags) of the repository.
|
||||
With this argument being true, it's possible to load a `rev` from *any* `ref`.
|
||||
(Without setting this option, only `rev`s from the specified `ref` are supported).
|
||||
|
||||
Default: `false`
|
||||
)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"dirtyRev",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"dirtyShortRev",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"verifyCommit",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"keytype",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"publicKey",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"publicKeys",
|
||||
{},
|
||||
},
|
||||
};
|
||||
return attrs;
|
||||
}
|
||||
|
||||
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
|
||||
@@ -231,7 +384,7 @@ struct GitInputScheme : InputScheme
|
||||
if (auto ref = maybeGetStrAttr(attrs, "ref"); ref && !isLegalRefName(*ref))
|
||||
throw BadURL("invalid Git branch/tag name '%s'", *ref);
|
||||
|
||||
Input input{settings};
|
||||
Input input{};
|
||||
input.attrs = attrs;
|
||||
input.attrs["url"] = fixGitURL(getStrAttr(attrs, "url")).to_string();
|
||||
getShallowAttr(input);
|
||||
@@ -280,7 +433,8 @@ struct GitInputScheme : InputScheme
|
||||
return res;
|
||||
}
|
||||
|
||||
void clone(const Input & input, const Path & destDir) const override
|
||||
void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
|
||||
const override
|
||||
{
|
||||
auto repoInfo = getRepoInfo(input);
|
||||
|
||||
@@ -625,7 +779,7 @@ struct GitInputScheme : InputScheme
|
||||
}
|
||||
|
||||
std::pair<ref<SourceAccessor>, Input>
|
||||
getAccessorFromCommit(ref<Store> store, RepoInfo & repoInfo, Input && input) const
|
||||
getAccessorFromCommit(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const
|
||||
{
|
||||
assert(!repoInfo.workdirInfo.isDirty);
|
||||
|
||||
@@ -735,10 +889,10 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
auto rev = *input.getRev();
|
||||
|
||||
input.attrs.insert_or_assign("lastModified", getLastModified(*input.settings, repoInfo, repoDir, rev));
|
||||
input.attrs.insert_or_assign("lastModified", getLastModified(settings, repoInfo, repoDir, rev));
|
||||
|
||||
if (!getShallowAttr(input))
|
||||
input.attrs.insert_or_assign("revCount", getRevCount(*input.settings, repoInfo, repoDir, rev));
|
||||
input.attrs.insert_or_assign("revCount", getRevCount(settings, repoInfo, repoDir, rev));
|
||||
|
||||
printTalkative("using revision %s of repo '%s'", rev.gitRev(), repoInfo.locationToArg());
|
||||
|
||||
@@ -746,7 +900,8 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
bool exportIgnore = getExportIgnoreAttr(input);
|
||||
bool smudgeLfs = getLfsAttr(input);
|
||||
auto accessor = repo->getAccessor(rev, exportIgnore, "«" + input.to_string() + "»", smudgeLfs);
|
||||
auto accessor = repo->getAccessor(
|
||||
rev, {.exportIgnore = exportIgnore, .smudgeLfs = smudgeLfs}, "«" + input.to_string() + "»");
|
||||
|
||||
/* If the repo has submodules, fetch them and return a mounted
|
||||
input accessor consisting of the accessor for the top-level
|
||||
@@ -781,8 +936,8 @@ struct GitInputScheme : InputScheme
|
||||
attrs.insert_or_assign("submodules", Explicit<bool>{true});
|
||||
attrs.insert_or_assign("lfs", Explicit<bool>{smudgeLfs});
|
||||
attrs.insert_or_assign("allRefs", Explicit<bool>{true});
|
||||
auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs));
|
||||
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store);
|
||||
auto submoduleInput = fetchers::Input::fromAttrs(settings, std::move(attrs));
|
||||
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(settings, store);
|
||||
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
|
||||
mounts.insert_or_assign(submodule.path, submoduleAccessor);
|
||||
}
|
||||
@@ -799,7 +954,7 @@ struct GitInputScheme : InputScheme
|
||||
}
|
||||
|
||||
std::pair<ref<SourceAccessor>, Input>
|
||||
getAccessorFromWorkdir(ref<Store> store, RepoInfo & repoInfo, Input && input) const
|
||||
getAccessorFromWorkdir(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const
|
||||
{
|
||||
auto repoPath = repoInfo.getPath().value();
|
||||
|
||||
@@ -813,7 +968,7 @@ struct GitInputScheme : InputScheme
|
||||
auto exportIgnore = getExportIgnoreAttr(input);
|
||||
|
||||
ref<SourceAccessor> accessor =
|
||||
repo->getAccessor(repoInfo.workdirInfo, exportIgnore, makeNotAllowedError(repoPath));
|
||||
repo->getAccessor(repoInfo.workdirInfo, {.exportIgnore = exportIgnore}, makeNotAllowedError(repoPath));
|
||||
|
||||
/* If the repo has submodules, return a mounted input accessor
|
||||
consisting of the accessor for the top-level repo and the
|
||||
@@ -831,8 +986,8 @@ struct GitInputScheme : InputScheme
|
||||
// TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out
|
||||
// attrs.insert_or_assign("allRefs", Explicit<bool>{ true });
|
||||
|
||||
auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs));
|
||||
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store);
|
||||
auto submoduleInput = fetchers::Input::fromAttrs(settings, std::move(attrs));
|
||||
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(settings, store);
|
||||
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
|
||||
|
||||
/* If the submodule is dirty, mark this repo dirty as
|
||||
@@ -859,12 +1014,12 @@ struct GitInputScheme : InputScheme
|
||||
input.attrs.insert_or_assign("rev", rev.gitRev());
|
||||
if (!getShallowAttr(input)) {
|
||||
input.attrs.insert_or_assign(
|
||||
"revCount", rev == nullRev ? 0 : getRevCount(*input.settings, repoInfo, repoPath, rev));
|
||||
"revCount", rev == nullRev ? 0 : getRevCount(settings, repoInfo, repoPath, rev));
|
||||
}
|
||||
|
||||
verifyCommit(input, repo);
|
||||
} else {
|
||||
repoInfo.warnDirty(*input.settings);
|
||||
repoInfo.warnDirty(settings);
|
||||
|
||||
if (repoInfo.workdirInfo.headRev) {
|
||||
input.attrs.insert_or_assign("dirtyRev", repoInfo.workdirInfo.headRev->gitRev() + "-dirty");
|
||||
@@ -876,14 +1031,14 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
input.attrs.insert_or_assign(
|
||||
"lastModified",
|
||||
repoInfo.workdirInfo.headRev
|
||||
? getLastModified(*input.settings, repoInfo, repoPath, *repoInfo.workdirInfo.headRev)
|
||||
: 0);
|
||||
repoInfo.workdirInfo.headRev ? getLastModified(settings, repoInfo, repoPath, *repoInfo.workdirInfo.headRev)
|
||||
: 0);
|
||||
|
||||
return {accessor, std::move(input)};
|
||||
}
|
||||
|
||||
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
|
||||
std::pair<ref<SourceAccessor>, Input>
|
||||
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
|
||||
{
|
||||
Input input(_input);
|
||||
|
||||
@@ -899,13 +1054,13 @@ struct GitInputScheme : InputScheme
|
||||
}
|
||||
|
||||
auto [accessor, final] = input.getRef() || input.getRev() || !repoInfo.getPath()
|
||||
? getAccessorFromCommit(store, repoInfo, std::move(input))
|
||||
: getAccessorFromWorkdir(store, repoInfo, std::move(input));
|
||||
? getAccessorFromCommit(settings, store, repoInfo, std::move(input))
|
||||
: getAccessorFromWorkdir(settings, store, repoInfo, std::move(input));
|
||||
|
||||
return {accessor, std::move(final)};
|
||||
}
|
||||
|
||||
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override
|
||||
std::optional<std::string> getFingerprint(Store & store, const Input & input) const override
|
||||
{
|
||||
auto makeFingerprint = [&](const Hash & rev) {
|
||||
return rev.gitRev() + (getSubmodulesAttr(input) ? ";s" : "") + (getExportIgnoreAttr(input) ? ";e" : "")
|
||||
@@ -936,7 +1091,7 @@ struct GitInputScheme : InputScheme
|
||||
}
|
||||
}
|
||||
|
||||
bool isLocked(const Input & input) const override
|
||||
bool isLocked(const Settings & settings, const Input & input) const override
|
||||
{
|
||||
auto rev = input.getRev();
|
||||
return rev && rev != nullRev;
|
||||
|
||||
@@ -48,7 +48,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
auto size = path.size();
|
||||
if (size == 3) {
|
||||
if (std::regex_match(path[2], revRegex))
|
||||
rev = parseGitHash(path[2]);
|
||||
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
|
||||
else if (isLegalRefName(path[2]))
|
||||
ref = path[2];
|
||||
else
|
||||
@@ -74,7 +74,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
if (name == "rev") {
|
||||
if (rev)
|
||||
throw BadURL("URL '%s' contains multiple commit hashes", url);
|
||||
rev = parseGitHash(value);
|
||||
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
|
||||
} else if (name == "ref") {
|
||||
if (!isLegalRefName(value))
|
||||
throw BadURL("URL '%s' contains an invalid branch/tag name", url);
|
||||
@@ -92,7 +92,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
if (ref && rev)
|
||||
throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url, *ref, rev->gitRev());
|
||||
|
||||
Input input{settings};
|
||||
Input input{};
|
||||
input.attrs.insert_or_assign("type", std::string{schemeName()});
|
||||
input.attrs.insert_or_assign("owner", path[0]);
|
||||
input.attrs.insert_or_assign("repo", path[1]);
|
||||
@@ -110,18 +110,43 @@ struct GitArchiveInputScheme : InputScheme
|
||||
return input;
|
||||
}
|
||||
|
||||
StringSet allowedAttrs() const override
|
||||
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
|
||||
{
|
||||
return {
|
||||
"owner",
|
||||
"repo",
|
||||
"ref",
|
||||
"rev",
|
||||
"narHash",
|
||||
"lastModified",
|
||||
"host",
|
||||
"treeHash",
|
||||
static const std::map<std::string, AttributeInfo> attrs = {
|
||||
{
|
||||
"owner",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"repo",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"ref",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"rev",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"narHash",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"lastModified",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"host",
|
||||
{},
|
||||
},
|
||||
{
|
||||
"treeHash",
|
||||
{},
|
||||
},
|
||||
};
|
||||
return attrs;
|
||||
}
|
||||
|
||||
std::optional<Input> inputFromAttrs(const fetchers::Settings & settings, const Attrs & attrs) const override
|
||||
@@ -129,7 +154,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
getStrAttr(attrs, "owner");
|
||||
getStrAttr(attrs, "repo");
|
||||
|
||||
Input input{settings};
|
||||
Input input{};
|
||||
input.attrs = attrs;
|
||||
return input;
|
||||
}
|
||||
@@ -233,9 +258,9 @@ struct GitArchiveInputScheme : InputScheme
|
||||
std::optional<Hash> treeHash;
|
||||
};
|
||||
|
||||
virtual RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const = 0;
|
||||
virtual RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const = 0;
|
||||
|
||||
virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;
|
||||
virtual DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const = 0;
|
||||
|
||||
struct TarballInfo
|
||||
{
|
||||
@@ -243,7 +268,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
time_t lastModified;
|
||||
};
|
||||
|
||||
std::pair<Input, TarballInfo> downloadArchive(ref<Store> store, Input input) const
|
||||
std::pair<Input, TarballInfo> downloadArchive(const Settings & settings, Store & store, Input input) const
|
||||
{
|
||||
if (!maybeGetStrAttr(input.attrs, "ref"))
|
||||
input.attrs.insert_or_assign("ref", "HEAD");
|
||||
@@ -252,7 +277,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
|
||||
auto rev = input.getRev();
|
||||
if (!rev) {
|
||||
auto refInfo = getRevFromRef(store, input);
|
||||
auto refInfo = getRevFromRef(settings, store, input);
|
||||
rev = refInfo.rev;
|
||||
upstreamTreeHash = refInfo.treeHash;
|
||||
debug("HEAD revision for '%s' is %s", input.to_string(), refInfo.rev.gitRev());
|
||||
@@ -261,7 +286,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
input.attrs.erase("ref");
|
||||
input.attrs.insert_or_assign("rev", rev->gitRev());
|
||||
|
||||
auto cache = input.settings->getCache();
|
||||
auto cache = settings.getCache();
|
||||
|
||||
Cache::Key treeHashKey{"gitRevToTreeHash", {{"rev", rev->gitRev()}}};
|
||||
Cache::Key lastModifiedKey{"gitRevToLastModified", {{"rev", rev->gitRev()}}};
|
||||
@@ -270,7 +295,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) {
|
||||
auto treeHash = getRevAttr(*treeHashAttrs, "treeHash");
|
||||
auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified");
|
||||
if (getTarballCache()->hasObject(treeHash))
|
||||
if (settings.getTarballCache()->hasObject(treeHash))
|
||||
return {std::move(input), TarballInfo{.treeHash = treeHash, .lastModified = (time_t) lastModified}};
|
||||
else
|
||||
debug("Git tree with hash '%s' has disappeared from the cache, refetching...", treeHash.gitRev());
|
||||
@@ -278,7 +303,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
}
|
||||
|
||||
/* Stream the tarball into the tarball cache. */
|
||||
auto url = getDownloadUrl(input);
|
||||
auto url = getDownloadUrl(settings, input);
|
||||
|
||||
auto source = sinkToSource([&](Sink & sink) {
|
||||
FileTransferRequest req(url.url);
|
||||
@@ -290,7 +315,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
*logger, lvlInfo, actUnknown, fmt("unpacking '%s' into the Git cache", input.to_string()));
|
||||
|
||||
TarArchive archive{*source};
|
||||
auto tarballCache = getTarballCache();
|
||||
auto tarballCache = settings.getTarballCache();
|
||||
auto parseSink = tarballCache->getFileSystemObjectSink();
|
||||
auto lastModified = unpackTarfileToSink(archive, *parseSink);
|
||||
auto tree = parseSink->flush();
|
||||
@@ -315,28 +340,29 @@ struct GitArchiveInputScheme : InputScheme
|
||||
return {std::move(input), tarballInfo};
|
||||
}
|
||||
|
||||
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
|
||||
std::pair<ref<SourceAccessor>, Input>
|
||||
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
|
||||
{
|
||||
auto [input, tarballInfo] = downloadArchive(store, _input);
|
||||
auto [input, tarballInfo] = downloadArchive(settings, store, _input);
|
||||
|
||||
#if 0
|
||||
input.attrs.insert_or_assign("treeHash", tarballInfo.treeHash.gitRev());
|
||||
#endif
|
||||
input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified));
|
||||
|
||||
auto accessor = getTarballCache()->getAccessor(tarballInfo.treeHash, false, "«" + input.to_string() + "»");
|
||||
auto accessor =
|
||||
settings.getTarballCache()->getAccessor(tarballInfo.treeHash, {}, "«" + input.to_string() + "»");
|
||||
|
||||
return {accessor, input};
|
||||
}
|
||||
|
||||
bool isLocked(const Input & input) const override
|
||||
bool isLocked(const Settings & settings, const Input & input) const override
|
||||
{
|
||||
/* Since we can't verify the integrity of the tarball from the
|
||||
Git revision alone, we also require a NAR hash for
|
||||
locking. FIXME: in the future, we may want to require a Git
|
||||
tree hash instead of a NAR hash. */
|
||||
return input.getRev().has_value()
|
||||
&& (input.settings->trustTarballsFromGitForges || input.getNarHash().has_value());
|
||||
return input.getRev().has_value() && (settings.trustTarballsFromGitForges || input.getNarHash().has_value());
|
||||
}
|
||||
|
||||
std::optional<ExperimentalFeature> experimentalFeature() const override
|
||||
@@ -344,7 +370,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
return Xp::Flakes;
|
||||
}
|
||||
|
||||
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override
|
||||
std::optional<std::string> getFingerprint(Store & store, const Input & input) const override
|
||||
{
|
||||
if (auto rev = input.getRev())
|
||||
return rev->gitRev();
|
||||
@@ -360,6 +386,12 @@ struct GitHubInputScheme : GitArchiveInputScheme
|
||||
return "github";
|
||||
}
|
||||
|
||||
std::string schemeDescription() const override
|
||||
{
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
|
||||
{
|
||||
// Github supports PAT/OAuth2 tokens and HTTP Basic
|
||||
@@ -386,7 +418,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
|
||||
return getStrAttr(input.attrs, "repo");
|
||||
}
|
||||
|
||||
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
|
||||
RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
|
||||
{
|
||||
auto host = getHost(input);
|
||||
auto url = fmt(
|
||||
@@ -396,22 +428,22 @@ struct GitHubInputScheme : GitArchiveInputScheme
|
||||
getRepo(input),
|
||||
*input.getRef());
|
||||
|
||||
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
|
||||
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
|
||||
|
||||
auto downloadResult = downloadFile(store, *input.settings, url, "source", headers);
|
||||
auto downloadResult = downloadFile(store, settings, url, "source", headers);
|
||||
auto json = nlohmann::json::parse(
|
||||
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
|
||||
store.requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
|
||||
|
||||
return RefInfo{
|
||||
.rev = parseGitHash(std::string{json["sha"]}),
|
||||
.treeHash = parseGitHash(std::string{json["commit"]["tree"]["sha"]})};
|
||||
.rev = Hash::parseAny(std::string{json["sha"]}, HashAlgorithm::SHA1),
|
||||
.treeHash = Hash::parseAny(std::string{json["commit"]["tree"]["sha"]}, HashAlgorithm::SHA1)};
|
||||
}
|
||||
|
||||
DownloadUrl getDownloadUrl(const Input & input) const override
|
||||
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
|
||||
{
|
||||
auto host = getHost(input);
|
||||
|
||||
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
|
||||
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
|
||||
|
||||
// If we have no auth headers then we default to the public archive
|
||||
// urls so we do not run into rate limits.
|
||||
@@ -425,12 +457,13 @@ struct GitHubInputScheme : GitArchiveInputScheme
|
||||
return DownloadUrl{parseURL(url), headers};
|
||||
}
|
||||
|
||||
void clone(const Input & input, const Path & destDir) const override
|
||||
void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
|
||||
const override
|
||||
{
|
||||
auto host = getHost(input);
|
||||
Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input)))
|
||||
Input::fromURL(settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input)))
|
||||
.applyOverrides(input.getRef(), input.getRev())
|
||||
.clone(destDir);
|
||||
.clone(settings, store, destDir);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -441,6 +474,12 @@ struct GitLabInputScheme : GitArchiveInputScheme
|
||||
return "gitlab";
|
||||
}
|
||||
|
||||
std::string schemeDescription() const override
|
||||
{
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
|
||||
{
|
||||
// Gitlab supports 4 kinds of authorization, two of which are
|
||||
@@ -460,7 +499,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
|
||||
return std::make_pair(token.substr(0, fldsplit), token.substr(fldsplit + 1));
|
||||
}
|
||||
|
||||
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
|
||||
RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
|
||||
{
|
||||
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
|
||||
// See rate limiting note below
|
||||
@@ -471,14 +510,14 @@ struct GitLabInputScheme : GitArchiveInputScheme
|
||||
getStrAttr(input.attrs, "repo"),
|
||||
*input.getRef());
|
||||
|
||||
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
|
||||
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
|
||||
|
||||
auto downloadResult = downloadFile(store, *input.settings, url, "source", headers);
|
||||
auto downloadResult = downloadFile(store, settings, url, "source", headers);
|
||||
auto json = nlohmann::json::parse(
|
||||
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
|
||||
store.requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
|
||||
|
||||
if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) {
|
||||
return RefInfo{.rev = parseGitHash(std::string(json[0]["id"]))};
|
||||
return RefInfo{.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)};
|
||||
}
|
||||
if (json.is_array() && json.size() == 0) {
|
||||
throw Error("No commits returned by GitLab API -- does the git ref really exist?");
|
||||
@@ -487,7 +526,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
|
||||
}
|
||||
}
|
||||
|
||||
DownloadUrl getDownloadUrl(const Input & input) const override
|
||||
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
|
||||
{
|
||||
// This endpoint has a rate limit threshold that may be
|
||||
// server-specific and vary based whether the user is
|
||||
@@ -502,19 +541,20 @@ struct GitLabInputScheme : GitArchiveInputScheme
|
||||
getStrAttr(input.attrs, "repo"),
|
||||
input.getRev()->to_string(HashFormat::Base16, false));
|
||||
|
||||
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
|
||||
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
|
||||
return DownloadUrl{parseURL(url), headers};
|
||||
}
|
||||
|
||||
void clone(const Input & input, const Path & destDir) const override
|
||||
void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
|
||||
const override
|
||||
{
|
||||
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
|
||||
// FIXME: get username somewhere
|
||||
Input::fromURL(
|
||||
*input.settings,
|
||||
settings,
|
||||
fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
|
||||
.applyOverrides(input.getRef(), input.getRev())
|
||||
.clone(destDir);
|
||||
.clone(settings, store, destDir);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -525,6 +565,12 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
return "sourcehut";
|
||||
}
|
||||
|
||||
std::string schemeDescription() const override
|
||||
{
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
|
||||
{
|
||||
// SourceHut supports both PAT and OAuth2. See
|
||||
@@ -535,7 +581,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
// Once it is implemented, however, should work as expected.
|
||||
}
|
||||
|
||||
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
|
||||
RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
|
||||
{
|
||||
// TODO: In the future, when the sourcehut graphql API is implemented for mercurial
|
||||
// and with anonymous access, this method should use it instead.
|
||||
@@ -546,12 +592,12 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
auto base_url =
|
||||
fmt("https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"));
|
||||
|
||||
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
|
||||
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
|
||||
|
||||
std::string refUri;
|
||||
if (ref == "HEAD") {
|
||||
auto downloadFileResult = downloadFile(store, *input.settings, fmt("%s/HEAD", base_url), "source", headers);
|
||||
auto contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
|
||||
auto downloadFileResult = downloadFile(store, settings, fmt("%s/HEAD", base_url), "source", headers);
|
||||
auto contents = store.requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
|
||||
|
||||
auto remoteLine = git::parseLsRemoteLine(getLine(contents).first);
|
||||
if (!remoteLine) {
|
||||
@@ -563,9 +609,8 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
}
|
||||
std::regex refRegex(refUri);
|
||||
|
||||
auto downloadFileResult =
|
||||
downloadFile(store, *input.settings, fmt("%s/info/refs", base_url), "source", headers);
|
||||
auto contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
|
||||
auto downloadFileResult = downloadFile(store, settings, fmt("%s/info/refs", base_url), "source", headers);
|
||||
auto contents = store.requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
|
||||
std::istringstream is(contents);
|
||||
|
||||
std::string line;
|
||||
@@ -579,10 +624,10 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
if (!id)
|
||||
throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref);
|
||||
|
||||
return RefInfo{.rev = parseGitHash(*id)};
|
||||
return RefInfo{.rev = Hash::parseAny(*id, HashAlgorithm::SHA1)};
|
||||
}
|
||||
|
||||
DownloadUrl getDownloadUrl(const Input & input) const override
|
||||
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
|
||||
{
|
||||
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
|
||||
auto url =
|
||||
@@ -592,18 +637,19 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
getStrAttr(input.attrs, "repo"),
|
||||
input.getRev()->to_string(HashFormat::Base16, false));
|
||||
|
||||
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
|
||||
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
|
||||
return DownloadUrl{parseURL(url), headers};
|
||||
}
|
||||
|
||||
void clone(const Input & input, const Path & destDir) const override
|
||||
void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
|
||||
const override
|
||||
{
|
||||
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
|
||||
Input::fromURL(
|
||||
*input.settings,
|
||||
settings,
|
||||
fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
|
||||
.applyOverrides(input.getRef(), input.getRev())
|
||||
.clone(destDir);
|
||||
.clone(settings, store, destDir);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct GitRepo;
|
||||
|
||||
}
|
||||
|
||||
namespace nix::fetchers {
|
||||
|
||||
struct Cache;
|
||||
@@ -125,6 +131,8 @@ struct Settings : public Config
|
||||
|
||||
ref<Cache> getCache() const;
|
||||
|
||||
ref<GitRepo> getTarballCache() const;
|
||||
|
||||
private:
|
||||
mutable Sync<std::shared_ptr<Cache>> _cache;
|
||||
};
|
||||
|
||||
@@ -36,13 +36,6 @@ struct Input
|
||||
{
|
||||
friend struct InputScheme;
|
||||
|
||||
const Settings * settings;
|
||||
|
||||
Input(const Settings & settings)
|
||||
: settings{&settings}
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<InputScheme> scheme; // note: can be null
|
||||
Attrs attrs;
|
||||
|
||||
@@ -87,7 +80,7 @@ public:
|
||||
* attributes like a Git revision or NAR hash that uniquely
|
||||
* identify its contents.
|
||||
*/
|
||||
bool isLocked() const;
|
||||
bool isLocked(const Settings & settings) const;
|
||||
|
||||
/**
|
||||
* Only for relative path flakes, i.e. 'path:./foo', returns the
|
||||
@@ -120,7 +113,7 @@ public:
|
||||
* Fetch the entire input into the Nix store, returning the
|
||||
* location in the Nix store and the locked input.
|
||||
*/
|
||||
std::pair<StorePath, Input> fetchToStore(ref<Store> store) const;
|
||||
std::pair<StorePath, Input> fetchToStore(const Settings & settings, Store & store) const;
|
||||
|
||||
/**
|
||||
* Check the locking attributes in `result` against
|
||||
@@ -140,17 +133,17 @@ public:
|
||||
* input without copying it to the store. Also return a possibly
|
||||
* unlocked input.
|
||||
*/
|
||||
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store) const;
|
||||
std::pair<ref<SourceAccessor>, Input> getAccessor(const Settings & settings, Store & store) const;
|
||||
|
||||
private:
|
||||
|
||||
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(ref<Store> store) const;
|
||||
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(const Settings & settings, Store & store) const;
|
||||
|
||||
public:
|
||||
|
||||
Input applyOverrides(std::optional<std::string> ref, std::optional<Hash> rev) const;
|
||||
|
||||
void clone(const Path & destDir) const;
|
||||
void clone(const Settings & settings, Store & store, const std::filesystem::path & destDir) const;
|
||||
|
||||
std::optional<std::filesystem::path> getSourcePath() const;
|
||||
|
||||
@@ -180,7 +173,7 @@ public:
|
||||
*
|
||||
* This is not a stable identifier between Nix versions, but not guaranteed to change either.
|
||||
*/
|
||||
std::optional<std::string> getFingerprint(ref<Store> store) const;
|
||||
std::optional<std::string> getFingerprint(Store & store) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -210,20 +203,34 @@ struct InputScheme
|
||||
*/
|
||||
virtual std::string_view schemeName() const = 0;
|
||||
|
||||
/**
|
||||
* Longform description of this scheme, for documentation purposes.
|
||||
*/
|
||||
virtual std::string schemeDescription() const = 0;
|
||||
|
||||
// TODO remove these defaults
|
||||
struct AttributeInfo
|
||||
{
|
||||
const char * type = "String";
|
||||
bool required = true;
|
||||
const char * doc = "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Allowed attributes in an attribute set that is converted to an
|
||||
* input.
|
||||
* input, and documentation for each attribute.
|
||||
*
|
||||
* `type` is not included from this set, because the `type` field is
|
||||
* `type` is not included from this map, because the `type` field is
|
||||
parsed first to choose which scheme; `type` is always required.
|
||||
*/
|
||||
virtual StringSet allowedAttrs() const = 0;
|
||||
virtual const std::map<std::string, AttributeInfo> & allowedAttrs() const = 0;
|
||||
|
||||
virtual ParsedURL toURL(const Input & input) const;
|
||||
|
||||
virtual Input applyOverrides(const Input & input, std::optional<std::string> ref, std::optional<Hash> rev) const;
|
||||
|
||||
virtual void clone(const Input & input, const Path & destDir) const;
|
||||
virtual void
|
||||
clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir) const;
|
||||
|
||||
virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const;
|
||||
|
||||
@@ -233,7 +240,8 @@ struct InputScheme
|
||||
std::string_view contents,
|
||||
std::optional<std::string> commitMsg) const;
|
||||
|
||||
virtual std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & input) const = 0;
|
||||
virtual std::pair<ref<SourceAccessor>, Input>
|
||||
getAccessor(const Settings & settings, Store & store, const Input & input) const = 0;
|
||||
|
||||
/**
|
||||
* Is this `InputScheme` part of an experimental feature?
|
||||
@@ -245,12 +253,12 @@ struct InputScheme
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const
|
||||
virtual std::optional<std::string> getFingerprint(Store & store, const Input & input) const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
virtual bool isLocked(const Input & input) const
|
||||
virtual bool isLocked(const Settings & settings, const Input & input) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -269,7 +277,12 @@ struct InputScheme
|
||||
|
||||
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
|
||||
|
||||
nlohmann::json dumpRegisterInputSchemeInfo();
|
||||
using InputSchemeMap = std::map<std::string_view, std::shared_ptr<InputScheme>>;
|
||||
|
||||
/**
|
||||
* Use this for docs, not for finding a specific scheme
|
||||
*/
|
||||
const InputSchemeMap & getAllInputSchemes();
|
||||
|
||||
struct PublicKey
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "nix/util/serialise.hh"
|
||||
#include "nix/util/url.hh"
|
||||
|
||||
#include <git2-experimental/repository.h>
|
||||
#include <git2/repository.h>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ struct GitFileSystemObjectSink : ExtendedFileSystemObjectSink
|
||||
virtual Hash flush() = 0;
|
||||
};
|
||||
|
||||
struct GitAccessorOptions
|
||||
{
|
||||
bool exportIgnore = false;
|
||||
bool smudgeLfs = false;
|
||||
};
|
||||
|
||||
struct GitRepo
|
||||
{
|
||||
virtual ~GitRepo() {}
|
||||
@@ -89,10 +95,10 @@ struct GitRepo
|
||||
virtual bool hasObject(const Hash & oid) = 0;
|
||||
|
||||
virtual ref<SourceAccessor>
|
||||
getAccessor(const Hash & rev, bool exportIgnore, std::string displayPrefix, bool smudgeLfs = false) = 0;
|
||||
getAccessor(const Hash & rev, const GitAccessorOptions & options, std::string displayPrefix) = 0;
|
||||
|
||||
virtual ref<SourceAccessor>
|
||||
getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) = 0;
|
||||
virtual ref<SourceAccessor> getAccessor(
|
||||
const WorkdirInfo & wd, const GitAccessorOptions & options, MakeNotAllowedError makeNotAllowedError) = 0;
|
||||
|
||||
virtual ref<GitFileSystemObjectSink> getFileSystemObjectSink() = 0;
|
||||
|
||||
@@ -120,8 +126,6 @@ struct GitRepo
|
||||
virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0;
|
||||
};
|
||||
|
||||
ref<GitRepo> getTarballCache();
|
||||
|
||||
// A helper to ensure that the `git_*_free` functions get called.
|
||||
template<auto del>
|
||||
struct Deleter
|
||||
@@ -167,14 +171,4 @@ struct Setter
|
||||
*/
|
||||
bool isLegalRefName(const std::string & refName);
|
||||
|
||||
/**
|
||||
* Parse a base16-encoded git hash string and determine the hash
|
||||
* algorithm based on the length (40 chars = SHA1, 64 chars = SHA256).
|
||||
*
|
||||
* @note For Nix-native information we should *not* do length tricks,
|
||||
* but instead always rely on an explicit algorithm. This hack should be
|
||||
* only for foreign hash literals.
|
||||
*/
|
||||
Hash parseGitHash(std::string_view hashStr);
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace nix::fetchers {
|
||||
|
||||
enum class UseRegistries : int;
|
||||
struct Settings;
|
||||
|
||||
struct InputCache
|
||||
{
|
||||
@@ -14,7 +15,8 @@ struct InputCache
|
||||
Attrs extraAttrs;
|
||||
};
|
||||
|
||||
CachedResult getAccessor(ref<Store> store, const Input & originalInput, UseRegistries useRegistries);
|
||||
CachedResult
|
||||
getAccessor(const Settings & settings, Store & store, const Input & originalInput, UseRegistries useRegistries);
|
||||
|
||||
struct CachedInput
|
||||
{
|
||||
|
||||
@@ -13,8 +13,6 @@ namespace nix::fetchers {
|
||||
|
||||
struct Registry
|
||||
{
|
||||
const Settings & settings;
|
||||
|
||||
enum RegistryType {
|
||||
Flag = 0,
|
||||
User = 1,
|
||||
@@ -34,9 +32,8 @@ struct Registry
|
||||
|
||||
std::vector<Entry> entries;
|
||||
|
||||
Registry(const Settings & settings, RegistryType type)
|
||||
: settings{settings}
|
||||
, type{type}
|
||||
Registry(RegistryType type)
|
||||
: type{type}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -57,7 +54,7 @@ std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Pat
|
||||
|
||||
Path getUserRegistryPath();
|
||||
|
||||
Registries getRegistries(const Settings & settings, ref<Store> store);
|
||||
Registries getRegistries(const Settings & settings, Store & store);
|
||||
|
||||
void overrideRegistry(const Input & from, const Input & to, const Attrs & extraAttrs);
|
||||
|
||||
@@ -71,6 +68,7 @@ enum class UseRegistries : int {
|
||||
* Rewrite a flakeref using the registries. If `filter` is set, only
|
||||
* use the registries for which the filter function returns true.
|
||||
*/
|
||||
std::pair<Input, Attrs> lookupInRegistries(ref<Store> store, const Input & input, UseRegistries useRegistries);
|
||||
std::pair<Input, Attrs>
|
||||
lookupInRegistries(const Settings & settings, Store & store, const Input & input, UseRegistries useRegistries);
|
||||
|
||||
} // namespace nix::fetchers
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user