Compare commits
108 Commits
2
...
libgit2-25
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
156061409a | ||
|
|
6da56d7ed4 | ||
|
|
9846305fee | ||
|
|
cbe8ec7bd7 | ||
|
|
60667e9e5a | ||
|
|
318eea040f | ||
|
|
a091a8100a | ||
|
|
6ebaba50dd | ||
|
|
18941cb8fa | ||
|
|
a33fccf55a | ||
|
|
86f090837b | ||
|
|
08a8bae8b3 | ||
|
|
f715992346 | ||
|
|
98e61c6da9 | ||
|
|
479b6b73a9 | ||
|
|
3c2dcf42e9 | ||
|
|
5a97c00f29 | ||
|
|
2f3ec16793 | ||
|
|
3ee8e45f8e | ||
|
|
d262efc240 | ||
|
|
5b15544bdd | ||
|
|
ca787bc3e0 | ||
|
|
8cc3ede0fa | ||
|
|
caa196e31d | ||
|
|
0c37a62207 | ||
|
|
147e183c68 | ||
|
|
52b2909fd2 | ||
|
|
34c77ffe38 | ||
|
|
af8e44821e | ||
|
|
70fbd1cdf4 | ||
|
|
daace78239 | ||
|
|
d596b9754e | ||
|
|
c1317017e9 | ||
|
|
3f18cad5f1 | ||
|
|
41b62aa979 | ||
|
|
af41eccb31 | ||
|
|
e7b274f85a | ||
|
|
6bd92d47e5 | ||
|
|
b5302fc111 | ||
|
|
724086005a | ||
|
|
038d74edf7 | ||
|
|
b177354c35 | ||
|
|
2039235f6e | ||
|
|
0fd3b6fee6 | ||
|
|
b2f0472fe2 | ||
|
|
91af29f37a | ||
|
|
099af7578f | ||
|
|
948c89b367 | ||
|
|
7e84ce3904 | ||
|
|
a828cf777a | ||
|
|
687dd38998 | ||
|
|
62729ff472 | ||
|
|
a0d4714073 | ||
|
|
0507674a13 | ||
|
|
4f85cfe824 | ||
|
|
7d5567a8d7 | ||
|
|
3ed42cd354 | ||
|
|
4a888b4138 | ||
|
|
f2436a47bb | ||
|
|
83ddfaebf4 | ||
|
|
2b382b171c | ||
|
|
b7553378a4 | ||
|
|
d40f66109b | ||
|
|
9657feaf8c | ||
|
|
d05e85e5be | ||
|
|
9daef9cca2 | ||
|
|
341c42f321 | ||
|
|
631fb6c9ad | ||
|
|
11e19ee690 | ||
|
|
9f322398b4 | ||
|
|
e07510e504 | ||
|
|
ae15d4eaf3 | ||
|
|
469123eda1 | ||
|
|
389bcba97a | ||
|
|
3ef22a521d | ||
|
|
c8e24491c0 | ||
|
|
c3d4c5f69d | ||
|
|
43ce9da6ad | ||
|
|
144c66215b | ||
|
|
0d7b16da4d | ||
|
|
72d0f7b619 | ||
|
|
34ac1792f9 | ||
|
|
0586370e58 | ||
|
|
f63bb5b338 | ||
|
|
53b4ea6c85 | ||
|
|
7c85ac23e2 | ||
|
|
0539b58253 | ||
|
|
beace42e7a | ||
|
|
4a0ccc89d9 | ||
|
|
89fa8c09a9 | ||
|
|
5e025ce940 | ||
|
|
2f6c865e25 | ||
|
|
bd42092873 | ||
|
|
81a2809a52 | ||
|
|
3448d4fa4c | ||
|
|
965d6be7c1 | ||
|
|
040d1aae41 | ||
|
|
bf947bfc26 | ||
|
|
2d83bc6b83 | ||
|
|
233bd250d1 | ||
|
|
892eba4944 | ||
|
|
d8cec03fce | ||
|
|
c2609df08c | ||
|
|
ccc06451df | ||
|
|
3775a2a226 | ||
|
|
18941a2421 | ||
|
|
136825b4a2 | ||
|
|
28b73cabcc |
@@ -1,3 +1,4 @@
|
||||
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
||||
# Disable CodeRabbit auto-review to prevent verbose comments on PRs.
|
||||
# When enabled: false, CodeRabbit won't attempt reviews and won't post
|
||||
# "Review skipped" or other automated comments.
|
||||
@@ -12,3 +13,6 @@ reviews:
|
||||
tools:
|
||||
github-checks:
|
||||
enabled: false
|
||||
chat:
|
||||
art: false
|
||||
auto_reply: false
|
||||
|
||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -125,13 +125,13 @@ jobs:
|
||||
cat coverage-reports/index.txt >> $GITHUB_STEP_SUMMARY
|
||||
if: ${{ matrix.instrumented }}
|
||||
- name: Upload coverage reports
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: coverage-reports
|
||||
path: coverage-reports/
|
||||
if: ${{ matrix.instrumented }}
|
||||
- name: Upload installer tarball
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: installer-${{matrix.os}}
|
||||
path: out/*
|
||||
@@ -164,7 +164,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Download installer tarball
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: installer-${{matrix.os}}
|
||||
path: out
|
||||
@@ -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@c134e4c9e34bac6cab09cf239815f9339aaaf84e # v31.5.1
|
||||
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31.8.2
|
||||
if: ${{ !matrix.experimental-installer }}
|
||||
with:
|
||||
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
|
||||
|
||||
@@ -107,12 +107,29 @@ rec {
|
||||
};
|
||||
};
|
||||
|
||||
disable =
|
||||
let
|
||||
inherit (pkgs.stdenv) hostPlatform;
|
||||
in
|
||||
args@{
|
||||
pkgName,
|
||||
testName,
|
||||
test,
|
||||
}:
|
||||
lib.any (b: b) [
|
||||
# FIXME: Nix manual is impure and does not produce all settings on darwin
|
||||
(hostPlatform.isDarwin && pkgName == "nix-manual" && testName == "linkcheck")
|
||||
];
|
||||
|
||||
componentTests =
|
||||
(lib.concatMapAttrs (
|
||||
pkgName: pkg:
|
||||
lib.concatMapAttrs (testName: test: {
|
||||
"${componentTestsPrefix}${pkgName}-${testName}" = test;
|
||||
}) (pkg.tests or { })
|
||||
lib.concatMapAttrs (
|
||||
testName: test:
|
||||
lib.optionalAttrs (!disable { inherit pkgName testName test; }) {
|
||||
"${componentTestsPrefix}${pkgName}-${testName}" = test;
|
||||
}
|
||||
) (pkg.tests or { })
|
||||
) nixComponentsInstrumented)
|
||||
// lib.optionalAttrs (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) {
|
||||
"${componentTestsPrefix}nix-functional-tests" = nixComponentsInstrumented.nix-functional-tests;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
def transform_anchors_html:
|
||||
. | gsub($empty_anchor_regex; "<a name=\"" + .anchor + "\"></a>")
|
||||
. | gsub($empty_anchor_regex; "<a id=\"" + .anchor + "\"></a>")
|
||||
| gsub($anchor_regex; "<a href=\"#" + .anchor + "\" id=\"" + .anchor + "\">" + .text + "</a>");
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ additional-css = ["custom.css"]
|
||||
additional-js = ["redirects.js"]
|
||||
edit-url-template = "https://github.com/NixOS/nix/tree/master/doc/manual/{path}"
|
||||
git-repository-url = "https://github.com/NixOS/nix"
|
||||
mathjax-support = true
|
||||
|
||||
# Handles replacing @docroot@ with a path to ./source relative to that markdown file,
|
||||
# {{#include handlebars}}, and the @generated@ syntax used within these. it mostly
|
||||
|
||||
@@ -24,9 +24,9 @@ let
|
||||
in
|
||||
concatStringsSep "\n" (map showEntry storesList);
|
||||
|
||||
"index.md" =
|
||||
replaceStrings [ "@store-types@" ] [ index ]
|
||||
(readFile ./source/store/types/index.md.in);
|
||||
"index.md" = replaceStrings [ "@store-types@" ] [ index ] (
|
||||
readFile ./source/store/types/index.md.in
|
||||
);
|
||||
|
||||
tableOfContents =
|
||||
let
|
||||
|
||||
@@ -92,6 +92,8 @@ manual = custom_target(
|
||||
(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
|
||||
# Remove Mathjax 2.7, because we will actually use MathJax 3.x
|
||||
find @2@/manual | grep .html | xargs sed -i -e '/2.7.1.MathJax.js/d'
|
||||
find @2@/manual -iname meson.build -delete
|
||||
'''.format(
|
||||
python.full_path(),
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
# Configuration Options
|
||||
|
||||
version,
|
||||
|
||||
# `tests` attribute
|
||||
testers,
|
||||
}:
|
||||
|
||||
let
|
||||
@@ -37,9 +40,11 @@ mkMesonDerivation (finalAttrs: {
|
||||
../../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/derived-path
|
||||
../../src/libstore-tests/data/path-info
|
||||
../../src/libstore-tests/data/nar-info
|
||||
../../src/libstore-tests/data/build-result
|
||||
# Too many different types of files to filter for now
|
||||
../../doc/manual
|
||||
./.
|
||||
@@ -87,6 +92,29 @@ mkMesonDerivation (finalAttrs: {
|
||||
echo "doc manual ''$out/share/doc/nix/manual" >> ''$out/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
/**
|
||||
The root of the HTML manual.
|
||||
E.g. "${nix-manual.site}/index.html" exists.
|
||||
*/
|
||||
passthru.site = finalAttrs.finalPackage + "/share/doc/nix/manual";
|
||||
|
||||
passthru.tests = {
|
||||
# https://nixos.org/manual/nixpkgs/stable/index.html#tester-lycheeLinkCheck
|
||||
linkcheck = testers.lycheeLinkCheck {
|
||||
inherit (finalAttrs.finalPackage) site;
|
||||
extraConfig = {
|
||||
exclude = [
|
||||
# Exclude auto-generated JSON schema documentation which has
|
||||
# auto-generated fragment IDs that don't match the link references
|
||||
".*/protocols/json/.*\\.html"
|
||||
# Exclude undocumented builtins
|
||||
".*/language/builtins\\.html#builtins-addErrorContext"
|
||||
".*/language/builtins\\.html#builtins-appendContext"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.all;
|
||||
};
|
||||
|
||||
9
doc/manual/rl-next/channels-subdomain.md
Normal file
9
doc/manual/rl-next/channels-subdomain.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
synopsis: Channel URLs migrated to channels.nixos.org subdomain
|
||||
prs: [14518]
|
||||
issues: [14517]
|
||||
---
|
||||
|
||||
Channel URLs have been updated from `https://nixos.org/channels/` to `https://channels.nixos.org/` throughout Nix.
|
||||
|
||||
The subdomain provides better reliability with IPv6 support and improved CDN distribution. The old domain apex (`nixos.org/channels/`) currently redirects to the new location but may be deprecated in the future.
|
||||
47
doc/manual/rl-next/json-format-changes.md
Normal file
47
doc/manual/rl-next/json-format-changes.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
synopsis: "JSON format changes for store path info and derivations"
|
||||
prs: []
|
||||
issues: []
|
||||
---
|
||||
|
||||
JSON formats for store path info and derivations have been updated with new versions and structured fields.
|
||||
|
||||
## Store Path Info JSON (Version 2)
|
||||
|
||||
The store path info JSON format has been updated from version 1 to version 2:
|
||||
|
||||
- **Added `version` field**:
|
||||
|
||||
All store path info JSON now includes `"version": 2`.
|
||||
|
||||
- **Structured `ca` field**:
|
||||
|
||||
Content address is now a structured JSON object instead of a string:
|
||||
|
||||
- Old: `"ca": "fixed:r:sha256:1abc..."`
|
||||
- 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.
|
||||
|
||||
**Affected command**: `nix path-info --json`
|
||||
|
||||
## Derivation JSON (Version 4)
|
||||
|
||||
The derivation JSON format has been updated from version 3 to version 4:
|
||||
|
||||
- **Restructured inputs**:
|
||||
|
||||
Inputs are now nested under an `inputs` object:
|
||||
|
||||
- Old: `"inputSrcs": [...], "inputDrvs": {...}`
|
||||
- New: `"inputs": {"srcs": [...], "drvs": {...}}`
|
||||
|
||||
- **Consistent content addresses**:
|
||||
|
||||
Floating content-addressed outputs now use structured JSON format.
|
||||
This is the same format as `ca` in in store path info (after the new version).
|
||||
|
||||
Version 3 and earlier formats are *not* accepted when reading.
|
||||
|
||||
**Affected command**: `nix derivation`, namely it's `show` and `add` sub-commands.
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
synopsis: "Improved S3 binary cache support via HTTP"
|
||||
prs: [13823, 14026, 14120, 14131, 14135, 14144, 14170, 14190, 14198, 14206, 14209, 14222, 14223, 13752]
|
||||
prs: [13752, 13823, 14026, 14120, 14131, 14135, 14144, 14170, 14190, 14198, 14206, 14209, 14222, 14223, 14330, 14333, 14335, 14336, 14337, 14350, 14356, 14357, 14374, 14375, 14376, 14377, 14391, 14393, 14420, 14421]
|
||||
issues: [13084, 12671, 11748, 12403]
|
||||
---
|
||||
|
||||
@@ -18,9 +18,23 @@ improvements:
|
||||
The new implementation requires curl >= 7.75.0 and `aws-crt-cpp` for credential
|
||||
management.
|
||||
|
||||
All existing S3 URL formats and parameters remain supported, with the notable
|
||||
exception of multi-part uploads, which are no longer supported.
|
||||
All existing S3 URL formats and parameters remain supported, however the store
|
||||
settings for configuring multipart uploads have changed:
|
||||
|
||||
- **`multipart-upload`** (default: `false`): Enable multipart uploads for large
|
||||
files. When enabled, files exceeding the multipart threshold will be uploaded
|
||||
in multiple parts.
|
||||
|
||||
- **`multipart-threshold`** (default: `100 MiB`): Minimum file size for using
|
||||
multipart uploads. Files smaller than this will use regular PUT requests.
|
||||
Only takes effect when `multipart-upload` is enabled.
|
||||
|
||||
- **`multipart-chunk-size`** (default: `5 MiB`): Size of each part in multipart
|
||||
uploads. Must be at least 5 MiB (AWS S3 requirement). Larger chunk sizes
|
||||
reduce the number of requests but use more memory.
|
||||
|
||||
- **`buffer-size`**: Has been replaced by `multipart-chunk-size` and is now an alias to it.
|
||||
|
||||
Note that this change also means Nix now supports S3 binary cache stores even
|
||||
if build without `aws-crt-cpp`, but only for public buckets which do not
|
||||
require auth.
|
||||
if built without `aws-crt-cpp`, but only for public buckets which do not
|
||||
require authentication.
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
- [Derivation Outputs and Types of Derivations](store/derivation/outputs/index.md)
|
||||
- [Content-addressing derivation outputs](store/derivation/outputs/content-address.md)
|
||||
- [Input-addressing derivation outputs](store/derivation/outputs/input-address.md)
|
||||
- [Build Trace](store/build-trace.md)
|
||||
- [Derivation Resolution](store/resolution.md)
|
||||
- [Building](store/building.md)
|
||||
- [Store Types](store/types/index.md)
|
||||
{{#include ./store/types/SUMMARY.md}}
|
||||
- [Appendix: Math notation](store/math-notation.md)
|
||||
- [Nix Language](language/index.md)
|
||||
- [Data Types](language/types.md)
|
||||
- [String context](language/string-context.md)
|
||||
@@ -123,9 +126,11 @@
|
||||
- [Store Object Info](protocols/json/store-object-info.md)
|
||||
- [Derivation](protocols/json/derivation.md)
|
||||
- [Deriving Path](protocols/json/deriving-path.md)
|
||||
- [Build Trace Entry](protocols/json/build-trace-entry.md)
|
||||
- [Build Result](protocols/json/build-result.md)
|
||||
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
|
||||
- [Store Path Specification](protocols/store-path.md)
|
||||
- [Nix Archive (NAR) Format](protocols/nix-archive.md)
|
||||
- [Nix Archive (NAR) Format](protocols/nix-archive/index.md)
|
||||
- [Derivation "ATerm" file format](protocols/derivation-aterm.md)
|
||||
- [C API](c-api.md)
|
||||
- [Glossary](glossary.md)
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
Channels are a mechanism for referencing remote Nix expressions and conveniently retrieving their latest version.
|
||||
|
||||
The moving parts of channels are:
|
||||
- The official channels listed at <https://nixos.org/channels>
|
||||
- The official channels listed at <https://channels.nixos.org>
|
||||
- The user-specific list of [subscribed channels](#subscribed-channels)
|
||||
- The [downloaded channel contents](#channels)
|
||||
- The [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path), set with the [`-I` option](#opt-i) or the [`NIX_PATH` environment variable](#env-NIX_PATH)
|
||||
- The [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path), set with the [`-I` option](#opt-I) or the [`NIX_PATH` environment variable](#env-NIX_PATH)
|
||||
|
||||
> **Note**
|
||||
>
|
||||
@@ -88,9 +88,9 @@ This command has the following operations:
|
||||
Subscribe to the Nixpkgs channel and run `hello` from the GNU Hello package:
|
||||
|
||||
```console
|
||||
$ nix-channel --add https://nixos.org/channels/nixpkgs-unstable
|
||||
$ nix-channel --add https://channels.nixos.org/nixpkgs-unstable
|
||||
$ nix-channel --list
|
||||
nixpkgs https://nixos.org/channels/nixpkgs
|
||||
nixpkgs https://channels.nixos.org/nixpkgs
|
||||
$ nix-channel --update
|
||||
$ nix-shell -p hello --run hello
|
||||
hello
|
||||
|
||||
@@ -22,7 +22,7 @@ left untouched; this is not an error. It is also not an error if an
|
||||
element of *args* matches no installed derivations.
|
||||
|
||||
For a description of how *args* is mapped to a set of store paths, see
|
||||
[`--install`](#operation---install). If *args* describes multiple
|
||||
[`--install`](./install.md). If *args* describes multiple
|
||||
store paths with the same symbolic name, only the one with the highest
|
||||
version is installed.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
This man page describes the command `nix-shell`, which is distinct from `nix
|
||||
shell`. For documentation on the latter, run `nix shell --help` or see `man
|
||||
nix3-shell`.
|
||||
nix3-env-shell`.
|
||||
|
||||
# Description
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ You can also build Nix for one of the [supported platforms](#platforms).
|
||||
This section assumes you are using Nix with the [`flakes`] and [`nix-command`] experimental features enabled.
|
||||
|
||||
[`flakes`]: @docroot@/development/experimental-features.md#xp-feature-flakes
|
||||
[`nix-command`]: @docroot@/development/experimental-features.md#xp-nix-command
|
||||
[`nix-command`]: @docroot@/development/experimental-features.md#xp-feature-nix-command
|
||||
|
||||
To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found:
|
||||
|
||||
@@ -256,7 +256,7 @@ You can use any of the other supported environments in place of `nix-cli-ccacheS
|
||||
## Editor integration
|
||||
|
||||
The `clangd` LSP server is installed by default on the `clang`-based `devShell`s.
|
||||
See [supported compilation environments](#compilation-environments) and instructions how to set up a shell [with flakes](#nix-with-flakes) or in [classic Nix](#classic-nix).
|
||||
See [supported compilation environments](#compilation-environments) and instructions how to set up a shell [with flakes](#building-nix-with-flakes) or in [classic Nix](#building-nix).
|
||||
|
||||
To use the LSP with your editor, you will want a `compile_commands.json` file telling `clangd` how we are compiling the code.
|
||||
Meson's configure always produces this inside the build directory.
|
||||
|
||||
@@ -119,7 +119,7 @@ This will:
|
||||
|
||||
3. Stop the program when the test fails, allowing the user to then issue arbitrary commands to GDB.
|
||||
|
||||
### Characterisation testing { #characaterisation-testing-unit }
|
||||
### Characterisation testing { #characterisation-testing-unit }
|
||||
|
||||
See [functional characterisation testing](#characterisation-testing-functional) for a broader discussion of characterisation testing.
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
|
||||
- [impure derivation]{#gloss-impure-derivation}
|
||||
|
||||
[An experimental feature](#@docroot@/development/experimental-features.md#xp-feature-impure-derivations) that allows derivations to be explicitly marked as impure,
|
||||
[An experimental feature](@docroot@/development/experimental-features.md#xp-feature-impure-derivations) that allows derivations to be explicitly marked as impure,
|
||||
so that they are always rebuilt, and their outputs not reused by subsequent calls to realise them.
|
||||
|
||||
- [Nix database]{#gloss-nix-database}
|
||||
@@ -279,7 +279,7 @@
|
||||
|
||||
See [References](@docroot@/store/store-object.md#references) for details.
|
||||
|
||||
- [referrer]{#gloss-reference}
|
||||
- [referrer]{#gloss-referrer}
|
||||
|
||||
A reversed edge from one [store object] to another.
|
||||
|
||||
@@ -367,8 +367,8 @@
|
||||
|
||||
Nix represents files as [file system objects][file system object], and how they belong together is encoded as [references][reference] between [store objects][store object] that contain these file system objects.
|
||||
|
||||
The [Nix language] allows denoting packages in terms of [attribute sets](@docroot@/language/types.md#attribute-set) containing:
|
||||
- attributes that refer to the files of a package, typically in the form of [derivation outputs](#output),
|
||||
The [Nix language] allows denoting packages in terms of [attribute sets](@docroot@/language/types.md#type-attrs) containing:
|
||||
- attributes that refer to the files of a package, typically in the form of [derivation outputs](#gloss-output),
|
||||
- attributes with metadata, such as information about how the package is supposed to be used.
|
||||
|
||||
The exact shape of these attribute sets is up to convention.
|
||||
@@ -383,7 +383,7 @@
|
||||
|
||||
[string]: ./language/types.md#type-string
|
||||
[path]: ./language/types.md#type-path
|
||||
[attribute name]: ./language/types.md#attribute-set
|
||||
[attribute name]: ./language/types.md#type-attrs
|
||||
|
||||
- [base directory]{#gloss-base-directory}
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ Here is more information on the `output*` attributes, and what values they may b
|
||||
|
||||
`outputHashAlgo` can only be `null` when `outputHash` follows the SRI format, because in that case the choice of hash algorithm is determined by `outputHash`.
|
||||
|
||||
- [`outputHash`]{#adv-attr-outputHashAlgo}; [`outputHash`]{#adv-attr-outputHashMode}
|
||||
- [`outputHash`]{#adv-attr-outputHash}
|
||||
|
||||
This will specify the output hash of the single output of a [fixed-output derivation].
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect
|
||||
- [`name`]{#attr-name} ([String](@docroot@/language/types.md#type-string))
|
||||
|
||||
A symbolic name for the derivation.
|
||||
See [derivation outputs](@docroot@/store/derivation/index.md#outputs) for what this is affects.
|
||||
See [derivation outputs](@docroot@/store/derivation/outputs/index.md#outputs) for what this is affects.
|
||||
|
||||
[store path]: @docroot@/store/store-path.md
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ An *identifier* is an [ASCII](https://en.wikipedia.org/wiki/ASCII) character seq
|
||||
|
||||
# Names
|
||||
|
||||
A *name* can be written as an [identifier](#identifier) or a [string literal](./string-literals.md).
|
||||
A *name* can be written as an [identifier](#identifiers) or a [string literal](./string-literals.md).
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
|
||||
@@ -137,7 +137,7 @@ This is an incomplete overview of language features, by example.
|
||||
</td>
|
||||
<td>
|
||||
|
||||
[Booleans](@docroot@/language/types.md#type-boolean)
|
||||
[Booleans](@docroot@/language/types.md#type-bool)
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@@ -245,7 +245,7 @@ This is an incomplete overview of language features, by example.
|
||||
</td>
|
||||
<td>
|
||||
|
||||
An [attribute set](@docroot@/language/types.md#attribute-set) with attributes named `x` and `y`
|
||||
An [attribute set](@docroot@/language/types.md#type-attrs) with attributes named `x` and `y`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@@ -285,7 +285,7 @@ This is an incomplete overview of language features, by example.
|
||||
</td>
|
||||
<td>
|
||||
|
||||
[Lists](@docroot@/language/types.md#list) with three elements.
|
||||
[Lists](@docroot@/language/types.md#type-list) with three elements.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@@ -369,7 +369,7 @@ This is an incomplete overview of language features, by example.
|
||||
</td>
|
||||
<td>
|
||||
|
||||
[Attribute selection](@docroot@/language/types.md#attribute-set) (evaluates to `1`)
|
||||
[Attribute selection](@docroot@/language/types.md#type-attrs) (evaluates to `1`)
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@@ -381,7 +381,7 @@ This is an incomplete overview of language features, by example.
|
||||
</td>
|
||||
<td>
|
||||
|
||||
[Attribute selection](@docroot@/language/types.md#attribute-set) with default (evaluates to `3`)
|
||||
[Attribute selection](@docroot@/language/types.md#type-attrs) with default (evaluates to `3`)
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -111,7 +111,7 @@ It creates an [attribute set] representing the string context, which can be insp
|
||||
|
||||
[`builtins.hasContext`]: ./builtins.md#builtins-hasContext
|
||||
[`builtins.getContext`]: ./builtins.md#builtins-getContext
|
||||
[attribute set]: ./types.md#attribute-set
|
||||
[attribute set]: ./types.md#type-attrs
|
||||
|
||||
## Clearing string contexts
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Such a construct is called *interpolated string*, and the expression inside is a
|
||||
|
||||
[string]: ./types.md#type-string
|
||||
[path]: ./types.md#type-path
|
||||
[attribute set]: ./types.md#attribute-set
|
||||
[attribute set]: ./types.md#type-attrs
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
|
||||
@@ -51,7 +51,7 @@ See [String literals](string-literals.md).
|
||||
|
||||
Path literals can also include [string interpolation], besides being [interpolated into other expressions].
|
||||
|
||||
[interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions
|
||||
[interpolated into other expressions]: ./string-interpolation.md#interpolated-expression
|
||||
|
||||
At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path.
|
||||
|
||||
@@ -235,7 +235,7 @@ of object-oriented programming, for example.
|
||||
|
||||
## Recursive sets
|
||||
|
||||
Recursive sets are like normal [attribute sets](./types.md#attribute-set), but the attributes can refer to each other.
|
||||
Recursive sets are like normal [attribute sets](./types.md#type-attrs), but the attributes can refer to each other.
|
||||
|
||||
> *rec-attrset* = `rec {` [ *name* `=` *expr* `;` `]`... `}`
|
||||
|
||||
@@ -287,7 +287,7 @@ This evaluates to `"foobar"`.
|
||||
|
||||
## Inheriting attributes
|
||||
|
||||
When defining an [attribute set](./types.md#attribute-set) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes).
|
||||
When defining an [attribute set](./types.md#type-attrs) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes).
|
||||
This can be shortened using the `inherit` keyword.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Derivation "ATerm" file format
|
||||
|
||||
For historical reasons, [store derivations][store derivation] are stored on-disk in [ATerm](https://homepages.cwi.nl/~daybuild/daily-books/technology/aterm-guide/aterm-guide.html) format.
|
||||
For historical reasons, [store derivations][store derivation] are stored on-disk in "Annotated Term" (ATerm) format
|
||||
([guide](https://homepages.cwi.nl/~daybuild/daily-books/technology/aterm-guide/aterm-guide.html),
|
||||
[paper](https://doi.org/10.1002/(SICI)1097-024X(200003)30:3%3C259::AID-SPE298%3E3.0.CO;2-Y)).
|
||||
|
||||
## The ATerm format used
|
||||
|
||||
|
||||
21
doc/manual/source/protocols/json/build-result.md
Normal file
21
doc/manual/source/protocols/json/build-result.md
Normal file
@@ -0,0 +1,21 @@
|
||||
{{#include build-result-v1-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Successful build
|
||||
|
||||
```json
|
||||
{{#include schema/build-result-v1/success.json}}
|
||||
```
|
||||
|
||||
### Failed build (output rejected)
|
||||
|
||||
```json
|
||||
{{#include schema/build-result-v1/output-rejected.json}}
|
||||
```
|
||||
|
||||
### Failed build (non-deterministic)
|
||||
|
||||
```json
|
||||
{{#include schema/build-result-v1/not-deterministic.json}}
|
||||
```
|
||||
27
doc/manual/source/protocols/json/build-trace-entry.md
Normal file
27
doc/manual/source/protocols/json/build-trace-entry.md
Normal file
@@ -0,0 +1,27 @@
|
||||
{{#include build-trace-entry-v1-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple build trace entry
|
||||
|
||||
```json
|
||||
{{#include schema/build-trace-entry-v1/simple.json}}
|
||||
```
|
||||
|
||||
### Build trace entry with dependencies
|
||||
|
||||
```json
|
||||
{{#include schema/build-trace-entry-v1/with-dependent-realisations.json}}
|
||||
```
|
||||
|
||||
### Build trace entry with signature
|
||||
|
||||
```json
|
||||
{{#include schema/build-trace-entry-v1/with-signature.json}}
|
||||
```
|
||||
|
||||
<!--
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v1.json)
|
||||
-->
|
||||
@@ -1,7 +1,7 @@
|
||||
{{#include derivation-v3-fixed.md}}
|
||||
{{#include derivation-v4-fixed.md}}
|
||||
|
||||
<!-- need to convert YAML to JSON first
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for Derivation v3](schema/derivation-v3.json)
|
||||
[JSON Schema for Derivation v3](schema/derivation-v4.json)
|
||||
-->
|
||||
|
||||
@@ -12,9 +12,11 @@ schemas = [
|
||||
'hash-v1',
|
||||
'content-address-v1',
|
||||
'store-path-v1',
|
||||
'store-object-info-v1',
|
||||
'derivation-v3',
|
||||
'store-object-info-v2',
|
||||
'derivation-v4',
|
||||
'deriving-path-v1',
|
||||
'build-trace-entry-v1',
|
||||
'build-result-v1',
|
||||
]
|
||||
|
||||
schema_files = files()
|
||||
|
||||
1
doc/manual/source/protocols/json/schema/build-result-v1
Symbolic link
1
doc/manual/source/protocols/json/schema/build-result-v1
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../src/libstore-tests/data/build-result
|
||||
136
doc/manual/source/protocols/json/schema/build-result-v1.yaml
Normal file
136
doc/manual/source/protocols/json/schema/build-result-v1.yaml
Normal file
@@ -0,0 +1,136 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-result-v1.json"
|
||||
title: Build Result
|
||||
description: |
|
||||
This schema describes the JSON representation of Nix's `BuildResult` type, which represents the result of building a derivation or substituting store paths.
|
||||
|
||||
Build results can represent either successful builds (with built outputs) or various types of failures.
|
||||
|
||||
oneOf:
|
||||
- "$ref": "#/$defs/success"
|
||||
- "$ref": "#/$defs/failure"
|
||||
type: object
|
||||
required:
|
||||
- success
|
||||
- status
|
||||
properties:
|
||||
timesBuilt:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: Times built
|
||||
description: |
|
||||
How many times this build was performed.
|
||||
|
||||
startTime:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: Start time
|
||||
description: |
|
||||
The start time of the build (or one of the rounds, if it was repeated), as a Unix timestamp.
|
||||
|
||||
stopTime:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: Stop time
|
||||
description: |
|
||||
The stop time of the build (or one of the rounds, if it was repeated), as a Unix timestamp.
|
||||
|
||||
cpuUser:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: User CPU time
|
||||
description: |
|
||||
User CPU time the build took, in microseconds.
|
||||
|
||||
cpuSystem:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: System CPU time
|
||||
description: |
|
||||
System CPU time the build took, in microseconds.
|
||||
|
||||
"$defs":
|
||||
success:
|
||||
type: object
|
||||
title: Successful Build Result
|
||||
description: |
|
||||
Represents a successful build with built outputs.
|
||||
required:
|
||||
- success
|
||||
- status
|
||||
- builtOutputs
|
||||
properties:
|
||||
success:
|
||||
const: true
|
||||
title: Success indicator
|
||||
description: |
|
||||
Always true for successful build results.
|
||||
|
||||
status:
|
||||
type: string
|
||||
title: Success status
|
||||
description: |
|
||||
Status string for successful builds.
|
||||
enum:
|
||||
- "Built"
|
||||
- "Substituted"
|
||||
- "AlreadyValid"
|
||||
- "ResolvesToAlreadyValid"
|
||||
|
||||
builtOutputs:
|
||||
type: object
|
||||
title: Built outputs
|
||||
description: |
|
||||
A mapping from output names to their build trace entries.
|
||||
additionalProperties:
|
||||
"$ref": "build-trace-entry-v1.yaml"
|
||||
|
||||
failure:
|
||||
type: object
|
||||
title: Failed Build Result
|
||||
description: |
|
||||
Represents a failed build with error information.
|
||||
required:
|
||||
- success
|
||||
- status
|
||||
- errorMsg
|
||||
properties:
|
||||
success:
|
||||
const: false
|
||||
title: Success indicator
|
||||
description: |
|
||||
Always false for failed build results.
|
||||
|
||||
status:
|
||||
type: string
|
||||
title: Failure status
|
||||
description: |
|
||||
Status string for failed builds.
|
||||
enum:
|
||||
- "PermanentFailure"
|
||||
- "InputRejected"
|
||||
- "OutputRejected"
|
||||
- "TransientFailure"
|
||||
- "CachedFailure"
|
||||
- "TimedOut"
|
||||
- "MiscFailure"
|
||||
- "DependencyFailed"
|
||||
- "LogLimitExceeded"
|
||||
- "NotDeterministic"
|
||||
- "NoSubstituters"
|
||||
- "HashMismatch"
|
||||
|
||||
errorMsg:
|
||||
type: string
|
||||
title: Error message
|
||||
description: |
|
||||
Information about the error if the build failed.
|
||||
|
||||
isNonDeterministic:
|
||||
type: boolean
|
||||
title: Non-deterministic flag
|
||||
description: |
|
||||
If timesBuilt > 1, whether some builds did not produce the same result.
|
||||
|
||||
Note that 'isNonDeterministic = false' does not mean the build is deterministic,
|
||||
just that we don't have evidence of non-determinism.
|
||||
1
doc/manual/source/protocols/json/schema/build-trace-entry-v1
Symbolic link
1
doc/manual/source/protocols/json/schema/build-trace-entry-v1
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../src/libstore-tests/data/realisation
|
||||
@@ -0,0 +1,74 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v1.json"
|
||||
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.
|
||||
|
||||
> **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
|
||||
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
|
||||
|
||||
additionalProperties: false
|
||||
@@ -1,203 +0,0 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/derivation-v3.json"
|
||||
title: Derivation
|
||||
description: |
|
||||
Experimental JSON representation of a Nix derivation (version 3).
|
||||
|
||||
This schema describes the JSON representation of Nix's `Derivation` type.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This JSON format is currently
|
||||
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
|
||||
> and subject to change.
|
||||
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- version
|
||||
- outputs
|
||||
- inputSrcs
|
||||
- inputDrvs
|
||||
- system
|
||||
- builder
|
||||
- args
|
||||
- env
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
title: Derivation name
|
||||
description: |
|
||||
The name of the derivation.
|
||||
Used when calculating store paths for the derivation’s outputs.
|
||||
|
||||
version:
|
||||
const: 3
|
||||
title: Format version (must be 3)
|
||||
description: |
|
||||
Must be `3`.
|
||||
This is a guard that allows us to continue evolving this format.
|
||||
The choice of `3` is fairly arbitrary, but corresponds to this informal version:
|
||||
|
||||
- Version 0: A-Term format
|
||||
|
||||
- Version 1: Original JSON format, with ugly `"r:sha256"` inherited from A-Term format.
|
||||
|
||||
- Version 2: Separate `method` and `hashAlgo` fields in output specs
|
||||
|
||||
- Version 3: Drop store dir from store paths, just include base name.
|
||||
|
||||
Note that while this format is experimental, the maintenance of versions is best-effort, and not promised to identify every change.
|
||||
|
||||
outputs:
|
||||
type: object
|
||||
title: Output specifications
|
||||
description: |
|
||||
Information about the output paths of the derivation.
|
||||
This is a JSON object with one member per output, where the key is the output name and the value is a JSON object as described.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```json
|
||||
> "outputs": {
|
||||
> "out": {
|
||||
> "method": "nar",
|
||||
> "hashAlgo": "sha256",
|
||||
> "hash": "6fc80dcc62179dbc12fc0b5881275898f93444833d21b89dfe5f7fbcbb1d0d62"
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
additionalProperties:
|
||||
"$ref": "#/$defs/output"
|
||||
|
||||
inputSrcs:
|
||||
type: array
|
||||
title: Input source paths
|
||||
description: |
|
||||
List of store paths on which this derivation depends.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```json
|
||||
> "inputSrcs": [
|
||||
> "47y241wqdhac3jm5l7nv0x4975mb1975-separate-debug-info.sh",
|
||||
> "56d0w71pjj9bdr363ym3wj1zkwyqq97j-fix-pop-var-context-error.patch"
|
||||
> ]
|
||||
> ```
|
||||
items:
|
||||
$ref: "store-path-v1.yaml"
|
||||
|
||||
inputDrvs:
|
||||
type: object
|
||||
title: Input derivations
|
||||
description: |
|
||||
Mapping of derivation paths to lists of output names they provide.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```json
|
||||
> "inputDrvs": {
|
||||
> "6lkh5yi7nlb7l6dr8fljlli5zfd9hq58-curl-7.73.0.drv": ["dev"],
|
||||
> "fn3kgnfzl5dzym26j8g907gq3kbm8bfh-unzip-6.0.drv": ["out"]
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> specifies that this derivation depends on the `dev` output of `curl`, and the `out` output of `unzip`.
|
||||
patternProperties:
|
||||
"^[0123456789abcdfghijklmnpqrsvwxyz]{32}-.+\\.drv$":
|
||||
title: Store Path
|
||||
description: |
|
||||
A store path to a derivation, mapped to the outputs of that derivation.
|
||||
oneOf:
|
||||
- "$ref": "#/$defs/outputNames"
|
||||
- "$ref": "#/$defs/dynamicOutputs"
|
||||
additionalProperties: false
|
||||
|
||||
system:
|
||||
type: string
|
||||
title: Build system type
|
||||
description: |
|
||||
The system type on which this derivation is to be built
|
||||
(e.g. `x86_64-linux`).
|
||||
|
||||
builder:
|
||||
type: string
|
||||
title: Build program path
|
||||
description: |
|
||||
Absolute path of the program used to perform the build.
|
||||
Typically this is the `bash` shell
|
||||
(e.g. `/nix/store/r3j288vpmczbl500w6zz89gyfa4nr0b1-bash-4.4-p23/bin/bash`).
|
||||
|
||||
args:
|
||||
type: array
|
||||
title: Builder arguments
|
||||
description: |
|
||||
Command-line arguments passed to the `builder`.
|
||||
items:
|
||||
type: string
|
||||
|
||||
env:
|
||||
type: object
|
||||
title: Environment variables
|
||||
description: |
|
||||
Environment variables passed to the `builder`.
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
structuredAttrs:
|
||||
title: Structured attributes
|
||||
description: |
|
||||
[Structured Attributes](@docroot@/store/derivation/index.md#structured-attrs), only defined if the derivation contains them.
|
||||
Structured attributes are JSON, and thus embedded as-is.
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
"$defs":
|
||||
output:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
$ref: "store-path-v1.yaml"
|
||||
title: Output path
|
||||
description: |
|
||||
The output path, if known in advance.
|
||||
|
||||
method:
|
||||
"$ref": "./content-address-v1.yaml#/$defs/method"
|
||||
description: |
|
||||
For an output which will be [content addressed](@docroot@/store/derivation/outputs/content-address.md), a string representing the [method](@docroot@/store/store-object/content-address.md) of content addressing that is chosen.
|
||||
See the linked original definition for further details.
|
||||
hashAlgo:
|
||||
title: Hash algorithm
|
||||
"$ref": "./hash-v1.yaml#/$defs/algorithm"
|
||||
|
||||
hash:
|
||||
type: string
|
||||
title: Expected hash value
|
||||
description: |
|
||||
For fixed-output derivations, the expected content hash in base-16.
|
||||
|
||||
outputName:
|
||||
type: string
|
||||
title: Output name
|
||||
description: Name of the derivation output to depend on
|
||||
|
||||
outputNames:
|
||||
type: array
|
||||
title: Output Names
|
||||
description: Set of names of derivation outputs to depend on
|
||||
items:
|
||||
"$ref": "#/$defs/outputName"
|
||||
|
||||
dynamicOutputs:
|
||||
type: object
|
||||
title: Dynamic Outputs
|
||||
description: |
|
||||
**Experimental feature**: [`dynamic-derivations`](@docroot@/development/experimental-features.md#xp-feature-dynamic-derivations)
|
||||
|
||||
This recursive data type allows for depending on outputs of outputs.
|
||||
properties:
|
||||
outputs:
|
||||
"$ref": "#/$defs/outputNames"
|
||||
dynamicOutputs:
|
||||
"$ref": "#/$defs/dynamicOutputs"
|
||||
299
doc/manual/source/protocols/json/schema/derivation-v4.yaml
Normal file
299
doc/manual/source/protocols/json/schema/derivation-v4.yaml
Normal file
@@ -0,0 +1,299 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/derivation-v4.json"
|
||||
title: Derivation
|
||||
description: |
|
||||
Experimental JSON representation of a Nix derivation (version 4).
|
||||
|
||||
This schema describes the JSON representation of Nix's `Derivation` type.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This JSON format is currently
|
||||
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
|
||||
> and subject to change.
|
||||
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- version
|
||||
- outputs
|
||||
- inputs
|
||||
- system
|
||||
- builder
|
||||
- args
|
||||
- env
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
title: Derivation name
|
||||
description: |
|
||||
The name of the derivation.
|
||||
Used when calculating store paths for the derivation’s outputs.
|
||||
|
||||
version:
|
||||
const: 4
|
||||
title: Format version (must be 4)
|
||||
description: |
|
||||
Must be `4`.
|
||||
This is a guard that allows us to continue evolving this format.
|
||||
The choice of `3` is fairly arbitrary, but corresponds to this informal version:
|
||||
|
||||
- Version 0: ATerm format
|
||||
|
||||
- Version 1: Original JSON format, with ugly `"r:sha256"` inherited from ATerm format.
|
||||
|
||||
- Version 2: Separate `method` and `hashAlgo` fields in output specs
|
||||
|
||||
- Version 3: Drop store dir from store paths, just include base name.
|
||||
|
||||
- Version 4: Two cleanups, batched together to lesson churn:
|
||||
|
||||
- Reorganize inputs into nested structure (`inputs.srcs` and `inputs.drvs`)
|
||||
|
||||
- Use canonical content address JSON format for floating content addressed derivation outputs.
|
||||
|
||||
Note that while this format is experimental, the maintenance of versions is best-effort, and not promised to identify every change.
|
||||
|
||||
outputs:
|
||||
type: object
|
||||
title: Output specifications
|
||||
description: |
|
||||
Information about the output paths of the derivation.
|
||||
This is a JSON object with one member per output, where the key is the output name and the value is a JSON object as described.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```json
|
||||
> "outputs": {
|
||||
> "out": {
|
||||
> "method": "nar",
|
||||
> "hashAlgo": "sha256",
|
||||
> "hash": "6fc80dcc62179dbc12fc0b5881275898f93444833d21b89dfe5f7fbcbb1d0d62"
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
additionalProperties:
|
||||
"$ref": "#/$defs/output/overall"
|
||||
|
||||
inputs:
|
||||
type: object
|
||||
title: Derivation inputs
|
||||
description: |
|
||||
Input dependencies for the derivation, organized into source paths and derivation dependencies.
|
||||
required:
|
||||
- srcs
|
||||
- drvs
|
||||
properties:
|
||||
srcs:
|
||||
type: array
|
||||
title: Input source paths
|
||||
description: |
|
||||
List of store paths on which this derivation depends.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```json
|
||||
> "srcs": [
|
||||
> "47y241wqdhac3jm5l7nv0x4975mb1975-separate-debug-info.sh",
|
||||
> "56d0w71pjj9bdr363ym3wj1zkwyqq97j-fix-pop-var-context-error.patch"
|
||||
> ]
|
||||
> ```
|
||||
items:
|
||||
$ref: "store-path-v1.yaml"
|
||||
drvs:
|
||||
type: object
|
||||
title: Input derivations
|
||||
description: |
|
||||
Mapping of derivation paths to lists of output names they provide.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```json
|
||||
> "drvs": {
|
||||
> "6lkh5yi7nlb7l6dr8fljlli5zfd9hq58-curl-7.73.0.drv": ["dev"],
|
||||
> "fn3kgnfzl5dzym26j8g907gq3kbm8bfh-unzip-6.0.drv": ["out"]
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> specifies that this derivation depends on the `dev` output of `curl`, and the `out` output of `unzip`.
|
||||
patternProperties:
|
||||
"^[0123456789abcdfghijklmnpqrsvwxyz]{32}-.+\\.drv$":
|
||||
title: Store Path
|
||||
description: |
|
||||
A store path to a derivation, mapped to the outputs of that derivation.
|
||||
oneOf:
|
||||
- "$ref": "#/$defs/outputNames"
|
||||
- "$ref": "#/$defs/dynamicOutputs"
|
||||
additionalProperties: false
|
||||
additionalProperties: false
|
||||
|
||||
system:
|
||||
type: string
|
||||
title: Build system type
|
||||
description: |
|
||||
The system type on which this derivation is to be built
|
||||
(e.g. `x86_64-linux`).
|
||||
|
||||
builder:
|
||||
type: string
|
||||
title: Build program path
|
||||
description: |
|
||||
Absolute path of the program used to perform the build.
|
||||
Typically this is the `bash` shell
|
||||
(e.g. `/nix/store/r3j288vpmczbl500w6zz89gyfa4nr0b1-bash-4.4-p23/bin/bash`).
|
||||
|
||||
args:
|
||||
type: array
|
||||
title: Builder arguments
|
||||
description: |
|
||||
Command-line arguments passed to the `builder`.
|
||||
items:
|
||||
type: string
|
||||
|
||||
env:
|
||||
type: object
|
||||
title: Environment variables
|
||||
description: |
|
||||
Environment variables passed to the `builder`.
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
structuredAttrs:
|
||||
title: Structured attributes
|
||||
description: |
|
||||
[Structured Attributes](@docroot@/store/derivation/index.md#structured-attrs), only defined if the derivation contains them.
|
||||
Structured attributes are JSON, and thus embedded as-is.
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
"$defs":
|
||||
output:
|
||||
overall:
|
||||
title: Derivation Output
|
||||
description: |
|
||||
A single output of a derivation, with different variants for different output types.
|
||||
oneOf:
|
||||
- "$ref": "#/$defs/output/inputAddressed"
|
||||
- "$ref": "#/$defs/output/caFixed"
|
||||
- "$ref": "#/$defs/output/caFloating"
|
||||
- "$ref": "#/$defs/output/deferred"
|
||||
- "$ref": "#/$defs/output/impure"
|
||||
|
||||
inputAddressed:
|
||||
title: Input-Addressed Output
|
||||
description: |
|
||||
The traditional non-fixed-output derivation type.
|
||||
The output path is determined from the derivation itself.
|
||||
|
||||
See [Input-addressing derivation outputs](@docroot@/store/derivation/outputs/input-address.md) for more details.
|
||||
type: object
|
||||
required:
|
||||
- path
|
||||
properties:
|
||||
path:
|
||||
$ref: "store-path-v1.yaml"
|
||||
title: Output path
|
||||
description: |
|
||||
The output path determined from the derivation itself.
|
||||
additionalProperties: false
|
||||
|
||||
caFixed:
|
||||
title: Fixed Content-Addressed Output
|
||||
description: |
|
||||
The output is content-addressed, and the content-address is fixed in advance.
|
||||
|
||||
See [Fixed-output content-addressing](@docroot@/store/derivation/outputs/content-address.md#fixed) for more details.
|
||||
"$ref": "./content-address-v1.yaml"
|
||||
required:
|
||||
- method
|
||||
- hash
|
||||
properties:
|
||||
method:
|
||||
description: |
|
||||
Method of content addressing used for this output.
|
||||
hash:
|
||||
title: Expected hash value
|
||||
description: |
|
||||
The expected content hash.
|
||||
additionalProperties: false
|
||||
|
||||
caFloating:
|
||||
title: Floating Content-Addressed Output
|
||||
description: |
|
||||
Floating-output derivations, whose outputs are content
|
||||
addressed, but not fixed, and so the output paths are dynamically calculated from
|
||||
whatever the output ends up being.
|
||||
|
||||
See [Floating Content-Addressing](@docroot@/store/derivation/outputs/content-address.md#floating) for more details.
|
||||
type: object
|
||||
required:
|
||||
- method
|
||||
- hashAlgo
|
||||
properties:
|
||||
method:
|
||||
"$ref": "./content-address-v1.yaml#/$defs/method"
|
||||
description: |
|
||||
Method of content addressing used for this output.
|
||||
hashAlgo:
|
||||
title: Hash algorithm
|
||||
"$ref": "./hash-v1.yaml#/$defs/algorithm"
|
||||
description: |
|
||||
What hash algorithm to use for the given method of content-addressing.
|
||||
additionalProperties: false
|
||||
|
||||
deferred:
|
||||
title: Deferred Output
|
||||
description: |
|
||||
Input-addressed output which depends on a (CA) derivation whose outputs (and thus their content-address
|
||||
are not yet known.
|
||||
type: object
|
||||
properties: {}
|
||||
additionalProperties: false
|
||||
|
||||
impure:
|
||||
title: Impure Output
|
||||
description: |
|
||||
Impure output which is just like a floating content-addressed output, but this derivation runs without sandboxing.
|
||||
As such, we don't record it in the build trace, under the assumption that if we need it again, we should rebuild it, as it might produce something different.
|
||||
required:
|
||||
- impure
|
||||
- method
|
||||
- hashAlgo
|
||||
properties:
|
||||
impure:
|
||||
const: true
|
||||
method:
|
||||
"$ref": "./content-address-v1.yaml#/$defs/method"
|
||||
description: |
|
||||
How the file system objects will be serialized for hashing.
|
||||
hashAlgo:
|
||||
title: Hash algorithm
|
||||
"$ref": "./hash-v1.yaml#/$defs/algorithm"
|
||||
description: |
|
||||
How the serialization will be hashed.
|
||||
additionalProperties: false
|
||||
|
||||
outputName:
|
||||
type: string
|
||||
title: Output name
|
||||
description: Name of the derivation output to depend on
|
||||
|
||||
outputNames:
|
||||
type: array
|
||||
title: Output Names
|
||||
description: Set of names of derivation outputs to depend on
|
||||
items:
|
||||
"$ref": "#/$defs/outputName"
|
||||
|
||||
dynamicOutputs:
|
||||
type: object
|
||||
title: Dynamic Outputs
|
||||
description: |
|
||||
**Experimental feature**: [`dynamic-derivations`](@docroot@/development/experimental-features.md#xp-feature-dynamic-derivations)
|
||||
|
||||
This recursive data type allows for depending on outputs of outputs.
|
||||
properties:
|
||||
outputs:
|
||||
"$ref": "#/$defs/outputNames"
|
||||
dynamicOutputs:
|
||||
"$ref": "#/$defs/dynamicOutputs"
|
||||
@@ -51,4 +51,4 @@ additionalProperties: false
|
||||
description: |
|
||||
The hash algorithm used to compute the hash value.
|
||||
|
||||
`blake3` is currently experimental and requires the [`blake-hashing`](@docroot@/development/experimental-features.md#xp-feature-blake-hashing) experimental feature.
|
||||
`blake3` is currently experimental and requires the [`blake-hashing`](@docroot@/development/experimental-features.md#xp-feature-blake3-hashes) experimental feature.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-object-info-v1.json"
|
||||
title: Store Object Info
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-object-info-v2.json"
|
||||
title: Store Object Info v2
|
||||
description: |
|
||||
Information about a [store object](@docroot@/store/store-object.md).
|
||||
|
||||
@@ -41,11 +41,27 @@ $defs:
|
||||
This is the minimal set of fields that describe what a store object contains.
|
||||
type: object
|
||||
required:
|
||||
- version
|
||||
- narHash
|
||||
- narSize
|
||||
- references
|
||||
- ca
|
||||
properties:
|
||||
version:
|
||||
type: integer
|
||||
const: 2
|
||||
title: Format version (must be 2)
|
||||
description: |
|
||||
Must be `2`.
|
||||
This is a guard that allows us to continue evolving this format.
|
||||
Here is the rough version history:
|
||||
|
||||
- Version 0: `.narinfo` line-oriented format
|
||||
|
||||
- Version 1: Original JSON format, with ugly `"r:sha256"` inherited from `.narinfo` format.
|
||||
|
||||
- Version 2: Use structured JSON type for `ca`
|
||||
|
||||
path:
|
||||
type: string
|
||||
title: Store Path
|
||||
@@ -76,7 +92,10 @@ $defs:
|
||||
type: string
|
||||
|
||||
ca:
|
||||
type: ["string", "null"]
|
||||
oneOf:
|
||||
- type: "null"
|
||||
const: null
|
||||
- "$ref": "./content-address-v1.yaml"
|
||||
title: Content Address
|
||||
description: |
|
||||
If the store object is [content-addressed](@docroot@/store/store-object/content-address.md),
|
||||
@@ -91,6 +110,7 @@ $defs:
|
||||
In other words, the same store object in different stores could have different values for these impure fields.
|
||||
type: object
|
||||
required:
|
||||
- version
|
||||
- narHash
|
||||
- narSize
|
||||
- references
|
||||
@@ -101,6 +121,7 @@ $defs:
|
||||
- ultimate
|
||||
- signatures
|
||||
properties:
|
||||
version: { $ref: "#/$defs/base/properties/version" }
|
||||
path: { $ref: "#/$defs/base/properties/path" }
|
||||
narHash: { $ref: "#/$defs/base/properties/narHash" }
|
||||
narSize: { $ref: "#/$defs/base/properties/narSize" }
|
||||
@@ -164,6 +185,7 @@ $defs:
|
||||
This download information, being specific to how the store object happens to be stored and transferred, is also considered to be non-intrinsic / impure.
|
||||
type: object
|
||||
required:
|
||||
- version
|
||||
- narHash
|
||||
- narSize
|
||||
- references
|
||||
@@ -179,6 +201,7 @@ $defs:
|
||||
- downloadHash
|
||||
- downloadSize
|
||||
properties:
|
||||
version: { $ref: "#/$defs/base/properties/version" }
|
||||
path: { $ref: "#/$defs/base/properties/path" }
|
||||
narHash: { $ref: "#/$defs/base/properties/narHash" }
|
||||
narSize: { $ref: "#/$defs/base/properties/narSize" }
|
||||
@@ -1,29 +1,29 @@
|
||||
{{#include store-object-info-v1-fixed.md}}
|
||||
{{#include store-object-info-v2-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Minimal store object (content-addressed)
|
||||
|
||||
```json
|
||||
{{#include schema/store-object-info-v1/pure.json}}
|
||||
{{#include schema/store-object-info-v2/pure.json}}
|
||||
```
|
||||
|
||||
### Store object with impure fields
|
||||
|
||||
```json
|
||||
{{#include schema/store-object-info-v1/impure.json}}
|
||||
{{#include schema/store-object-info-v2/impure.json}}
|
||||
```
|
||||
|
||||
### Minimal store object (empty)
|
||||
|
||||
```json
|
||||
{{#include schema/store-object-info-v1/empty_pure.json}}
|
||||
{{#include schema/store-object-info-v2/empty_pure.json}}
|
||||
```
|
||||
|
||||
### Store object with all impure fields
|
||||
|
||||
```json
|
||||
{{#include schema/store-object-info-v1/empty_impure.json}}
|
||||
{{#include schema/store-object-info-v2/empty_impure.json}}
|
||||
```
|
||||
|
||||
### NAR info (minimal)
|
||||
@@ -41,5 +41,5 @@
|
||||
<!-- need to convert YAML to JSON first
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for Store Object Info v1](schema/store-object-info-v1.json)
|
||||
[JSON Schema for Store Object Info v1](schema/store-object-info-v2.json)
|
||||
-->
|
||||
|
||||
@@ -4,7 +4,7 @@ This is the complete specification of the [Nix Archive] format.
|
||||
The Nix Archive format closely follows the abstract specification of a [file system object] tree,
|
||||
because it is designed to serialize exactly that data structure.
|
||||
|
||||
[Nix Archive]: @docroot@/store/file-system-object/content-address.md#nix-archive
|
||||
[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive
|
||||
[file system object]: @docroot@/store/file-system-object.md
|
||||
|
||||
The format of this specification is close to [Extended Backus–Naur form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form), with the exception of the `str(..)` function / parameterized rule, which length-prefixes and pads strings.
|
||||
@@ -41,3 +41,15 @@ The `str` function / parameterized rule is defined as follows:
|
||||
- `int(n)` = the 64-bit little endian representation of the number `n`
|
||||
|
||||
- `pad(s)` = the byte sequence `s`, padded with 0s to a multiple of 8 byte
|
||||
|
||||
## Kaitai Struct Specification
|
||||
|
||||
The Nix Archive (NAR) format is also formally described using [Kaitai Struct](https://kaitai.io/), an Interface Description Language (IDL) for defining binary data structures.
|
||||
|
||||
> Kaitai Struct provides a language-agnostic, machine-readable specification that can be compiled into parsers for various programming languages (e.g., C++, Python, Java, Rust).
|
||||
|
||||
```yaml
|
||||
{{#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.
|
||||
169
doc/manual/source/protocols/nix-archive/nar.ksy
Normal file
169
doc/manual/source/protocols/nix-archive/nar.ksy
Normal file
@@ -0,0 +1,169 @@
|
||||
meta:
|
||||
id: nix_nar
|
||||
title: Nix Archive (NAR)
|
||||
file-extension: nar
|
||||
endian: le
|
||||
doc: |
|
||||
Nix Archive (NAR) format. A simple, reproducible binary archive
|
||||
format used by the Nix package manager to serialize file system objects.
|
||||
doc-ref: 'https://nixos.org/manual/nix/stable/command-ref/nix-store.html#nar-format'
|
||||
|
||||
seq:
|
||||
- id: magic
|
||||
type: padded_str
|
||||
doc: "Magic string, must be 'nix-archive-1'."
|
||||
valid:
|
||||
expr: _.body == 'nix-archive-1'
|
||||
- id: root_node
|
||||
type: node
|
||||
doc: "The root of the archive, which is always a single node."
|
||||
|
||||
types:
|
||||
padded_str:
|
||||
doc: |
|
||||
A string, prefixed with its length (u8le) and
|
||||
padded with null bytes to the next 8-byte boundary.
|
||||
seq:
|
||||
- id: len_str
|
||||
type: u8
|
||||
- id: body
|
||||
type: str
|
||||
size: len_str
|
||||
encoding: 'ASCII'
|
||||
- id: padding
|
||||
size: (8 - (len_str % 8)) % 8
|
||||
|
||||
node:
|
||||
doc: "A single filesystem node (file, directory, or symlink)."
|
||||
seq:
|
||||
- id: open_paren
|
||||
type: padded_str
|
||||
doc: "Must be '(', a token starting the node definition."
|
||||
valid:
|
||||
expr: _.body == '('
|
||||
- id: type_key
|
||||
type: padded_str
|
||||
doc: "Must be 'type'."
|
||||
valid:
|
||||
expr: _.body == 'type'
|
||||
- id: type_val
|
||||
type: padded_str
|
||||
doc: "The type of the node: 'regular', 'directory', or 'symlink'."
|
||||
- id: body
|
||||
type:
|
||||
switch-on: type_val.body
|
||||
cases:
|
||||
"'directory'": type_directory
|
||||
"'regular'": type_regular
|
||||
"'symlink'": type_symlink
|
||||
- id: close_paren
|
||||
type: padded_str
|
||||
valid:
|
||||
expr: _.body == ')'
|
||||
if: "type_val.body != 'directory'"
|
||||
doc: "Must be ')', a token ending the node definition."
|
||||
|
||||
type_directory:
|
||||
doc: "A directory node, containing a list of entries. Entries must be ordered by their names."
|
||||
seq:
|
||||
- id: entries
|
||||
type: dir_entry
|
||||
repeat: until
|
||||
repeat-until: _.kind.body == ')'
|
||||
types:
|
||||
dir_entry:
|
||||
doc: "A single entry within a directory, or a terminator."
|
||||
seq:
|
||||
- id: kind
|
||||
type: padded_str
|
||||
valid:
|
||||
expr: _.body == 'entry' or _.body == ')'
|
||||
doc: "Must be 'entry' (for a child node) or '' (for terminator)."
|
||||
- id: open_paren
|
||||
type: padded_str
|
||||
valid:
|
||||
expr: _.body == '('
|
||||
if: 'kind.body == "entry"'
|
||||
- id: name_key
|
||||
type: padded_str
|
||||
valid:
|
||||
expr: _.body == 'name'
|
||||
if: 'kind.body == "entry"'
|
||||
- id: name
|
||||
type: padded_str
|
||||
if: 'kind.body == "entry"'
|
||||
- id: node_key
|
||||
type: padded_str
|
||||
valid:
|
||||
expr: _.body == 'node'
|
||||
if: 'kind.body == "entry"'
|
||||
- id: node
|
||||
type: node
|
||||
if: 'kind.body == "entry"'
|
||||
doc: "The child node, present only if kind is 'entry'."
|
||||
- id: close_paren
|
||||
type: padded_str
|
||||
valid:
|
||||
expr: _.body == ')'
|
||||
if: 'kind.body == "entry"'
|
||||
instances:
|
||||
is_terminator:
|
||||
value: kind.body == ')'
|
||||
|
||||
type_regular:
|
||||
doc: "A regular file node."
|
||||
seq:
|
||||
# Read attributes (like 'executable') until we hit 'contents'
|
||||
- id: attributes
|
||||
type: reg_attribute
|
||||
repeat: until
|
||||
repeat-until: _.key.body == "contents"
|
||||
# After the 'contents' token, read the file data
|
||||
- id: file_data
|
||||
type: file_content
|
||||
instances:
|
||||
is_executable:
|
||||
value: 'attributes[0].key.body == "executable"'
|
||||
doc: "True if the file has the 'executable' attribute."
|
||||
types:
|
||||
reg_attribute:
|
||||
doc: "An attribute of the file, e.g., 'executable' or 'contents'."
|
||||
seq:
|
||||
- id: key
|
||||
type: padded_str
|
||||
doc: "Attribute key, e.g., 'executable' or 'contents'."
|
||||
valid:
|
||||
expr: _.body == 'executable' or _.body == 'contents'
|
||||
- id: value
|
||||
type: padded_str
|
||||
if: 'key.body == "executable"'
|
||||
valid:
|
||||
expr: _.body == ''
|
||||
doc: "Must be '' if key is 'executable'."
|
||||
file_content:
|
||||
doc: "The raw data of the file, prefixed by length."
|
||||
seq:
|
||||
- id: len_contents
|
||||
type: u8
|
||||
# # This relies on the property of instances that they are lazily evaluated and cached.
|
||||
- size: 0
|
||||
if: nar_offset < 0
|
||||
- id: contents
|
||||
size: len_contents
|
||||
- id: padding
|
||||
size: (8 - (len_contents % 8)) % 8
|
||||
instances:
|
||||
nar_offset:
|
||||
value: _io.pos
|
||||
|
||||
type_symlink:
|
||||
doc: "A symbolic link node."
|
||||
seq:
|
||||
- id: target_key
|
||||
type: padded_str
|
||||
doc: "Must be 'target'."
|
||||
valid:
|
||||
expr: _.body == 'target'
|
||||
- id: target_val
|
||||
type: padded_str
|
||||
doc: "The destination path of the symlink."
|
||||
@@ -358,7 +358,7 @@ This release has the following new features:
|
||||
they are needed for evaluation.
|
||||
|
||||
- You can now use `channel:` as a short-hand for
|
||||
<https://nixos.org/channels//nixexprs.tar.xz>. For example,
|
||||
<https://nixos.org/channels//nixexprs.tar.xz> [now <https://channels.nixos.org//nixexprs.tar.xz>]. For example,
|
||||
`nix-build channel:nixos-15.09 -A hello` will build the GNU Hello
|
||||
package from the `nixos-15.09` channel. In the future, this may
|
||||
use Git to fetch updates more efficiently.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
- The `discard-references` feature has been stabilized.
|
||||
This means that the
|
||||
[unsafeDiscardReferences](@docroot@/development/experimental-features.md#xp-feature-discard-references)
|
||||
[unsafeDiscardReferences](@docroot@/language/advanced-attributes.md#adv-attr-unsafeDiscardReferences)
|
||||
attribute is no longer guarded by an experimental flag and can be used
|
||||
freely.
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
- `nix-shell` shebang lines now support single-quoted arguments.
|
||||
|
||||
- `builtins.fetchTree` is now its own experimental feature, [`fetch-tree`](@docroot@/development/experimental-features.md#xp-fetch-tree).
|
||||
This allows stabilising it independently of the rest of what is encompassed by [`flakes`](@docroot@/development/experimental-features.md#xp-fetch-tree).
|
||||
- `builtins.fetchTree` is now its own experimental feature, [`fetch-tree`](@docroot@/development/experimental-features.md#xp-feature-fetch-tree).
|
||||
This allows stabilising it independently of the rest of what is encompassed by [`flakes`](@docroot@/development/experimental-features.md#xp-feature-flakes).
|
||||
|
||||
- The interface for creating and updating lock files has been overhauled:
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
- Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722)
|
||||
|
||||
The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/development/cli-guideline.md#returning-future-proof-json).
|
||||
The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/development/json-guideline.md).
|
||||
In particular, the hash algorithm and content addressing method of content-addressed derivation outputs are now separated into two fields `hashAlgo` and `method`,
|
||||
rather than one field with an arcane `:`-separated format.
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
- Support unit prefixes in configuration settings [#10668](https://github.com/NixOS/nix/pull/10668)
|
||||
|
||||
Configuration settings in Nix now support unit prefixes, allowing for more intuitive and readable configurations. For example, you can now specify [`--min-free 1G`](@docroot@/command-ref/opt-common.md#opt-min-free) to set the minimum free space to 1 gigabyte.
|
||||
Configuration settings in Nix now support unit prefixes, allowing for more intuitive and readable configurations. For example, you can now specify [`--min-free 1G`](@docroot@/command-ref/conf-file.md#conf-min-free) to set the minimum free space to 1 gigabyte.
|
||||
|
||||
This enhancement was extracted from [#7851](https://github.com/NixOS/nix/pull/7851) and is also useful for PR [#10661](https://github.com/NixOS/nix/pull/10661).
|
||||
|
||||
|
||||
53
doc/manual/source/store/build-trace.md
Normal file
53
doc/manual/source/store/build-trace.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Build Trace
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This entire concept is currently
|
||||
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-ca-derivations)
|
||||
> and subject to change.
|
||||
|
||||
The *build trace* is a [memoization table](https://en.wikipedia.org/wiki/Memoization) for builds.
|
||||
It maps the inputs of builds to the outputs of builds.
|
||||
Concretely, that means it maps [derivations][derivation] to maps of [output] names to [store objects][store object].
|
||||
|
||||
In general the derivations used as a key should be [*resolved*](./resolution.md).
|
||||
A build trace with all-resolved-derivation keys is also called a *base build trace* for extra clarity.
|
||||
If all the resolved inputs of a derivation are content-addressed, that means the inputs will be fully determined, leaving no ambiguity for what build was performed.
|
||||
(Input-addressed inputs however are still ambiguous. They too should be locked down, but this is left as future work.)
|
||||
|
||||
Accordingly, to look up an unresolved derivation, one must first resolve it to get a resolved derivation.
|
||||
Resolving itself involves looking up entries in the build trace, so this is a mutually recursive process that will end up inspecting possibly many entries.
|
||||
|
||||
Except for the issue with input-addressed paths called out above, base build traces are trivially *coherent* -- incoherence is not possible.
|
||||
That means that the claims that each key-value base build try entry makes are independent, and no mapping invalidates another mapping.
|
||||
|
||||
Whether the mappings are *true*, i.e. the faithful recording of actual builds performed, is another matter.
|
||||
Coherence is about the multiple claims of the build trace being mutually consistent, not about whether the claims are individually true or false.
|
||||
|
||||
In general, there is no way to audit a build trace entry except for by performing the build again from scratch.
|
||||
And even in that case, a different result doesn't mean the original entry was a "lie", because the derivation being built may be non-deterministic.
|
||||
As such, the decision of whether to trust a counterparty's build trace is a fundamentally subject policy choice.
|
||||
Build trace entries are typically *signed* in order to enable arbitrary public-key-based trust polices.
|
||||
|
||||
## Derived build traces {#derived}
|
||||
|
||||
Implementations that wish to memoize the above may also keep additional *derived* build trace entries that do map unresolved derivations.
|
||||
But if they do so, they *must* also keep the underlying base entries with resolved derivation keys around.
|
||||
Firstly, this ensures that the derived entries are merely cache, which could be recomputed from scratch.
|
||||
Secondly, this ensures the coherence of the derived build trace.
|
||||
|
||||
Unlike with base build traces, incoherence with derived build traces is possible.
|
||||
The key ingredient is that derivation resolution is only deterministic with respect to a fixed base build trace.
|
||||
Without fixing the base build trace, it inherits the subjectivity of base build traces themselves.
|
||||
|
||||
Concretely, suppose there are three derivations \\(a\\), \\(b\\), and \\(c\\).
|
||||
Let \\(a\\) be a resolved derivation, but let \\(b\\) and \\(c\\) be unresolved and both take as an input an output of \\(a\\).
|
||||
Now suppose that derived entries are made for \\(b\\) and \\(c\\) based on two different entries of \\(a\\).
|
||||
(This could happen if \\(a\\) is non-deterministic, \\(a\\) and \\(b\\) are built in one store, \\(a\\) and \\(c\\) are built in another store, and then a third store substitutes from both of the first two stores.)
|
||||
|
||||
If trusting the derived build trace entries for \\(b\\) and \\(c\\) requires that each's underlying entry for \\(a\\) be also trusted, the two different mappings for \\(a\\) will be caught.
|
||||
However, if \\(b\\) and \\(c\\)'s entries can be combined in isolation, there will be nothing to catch the contradiction in their hidden assumptions about \\(a\\)'s output.
|
||||
|
||||
[derivation]: ./derivation/index.md
|
||||
[output]: ./derivation/outputs/index.md
|
||||
[store object]: @docroot@/store/store-object.md
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
- Once this is done, the derivation is *normalized*, replacing each input deriving path with its store path, which we now know from realising the input.
|
||||
|
||||
## Builder Execution
|
||||
## Builder Execution {#builder-execution}
|
||||
|
||||
The [`builder`](./derivation/index.md#builder) is executed as follows:
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ But rather than somehow scanning all the other fields for inputs, Nix requires t
|
||||
|
||||
### System {#system}
|
||||
|
||||
The system type on which the [`builder`](#attr-builder) executable is meant to be run.
|
||||
The system type on which the [`builder`](#builder) executable is meant to be run.
|
||||
|
||||
A necessary condition for Nix to schedule a given derivation on some [Nix instance] is for the "system" of that derivation to match that instance's [`system` configuration option] or [`extra-platforms` configuration option].
|
||||
|
||||
@@ -245,7 +245,7 @@ If those other derivations *also* abide by this common case (and likewise for tr
|
||||
> note the ".drv"
|
||||
> ```
|
||||
|
||||
## Extending the model to be higher-order
|
||||
## Extending the model to be higher-order {#dynamic}
|
||||
|
||||
**Experimental feature**: [`dynamic-derivations`](@docroot@/development/experimental-features.md#xp-feature-dynamic-derivations)
|
||||
|
||||
|
||||
@@ -167,10 +167,10 @@ It is only in the potential for that check to fail that they are different.
|
||||
>
|
||||
> In a future world where floating content-addressing is also stable, we in principle no longer need separate [fixed](#fixed) content-addressing.
|
||||
> Instead, we could always use floating content-addressing, and separately assert the precise value content address of a given store object to be used as an input (of another derivation).
|
||||
> A stand-alone assertion object of this sort is not yet implemented, but its possible creation is tracked in [Issue #11955](https://github.com/NixOS/nix/issues/11955).
|
||||
> A stand-alone assertion object of this sort is not yet implemented, but its possible creation is tracked in [issue #11955](https://github.com/NixOS/nix/issues/11955).
|
||||
>
|
||||
> In the current version of Nix, fixed outputs which fail their hash check are still registered as valid store objects, just not registered as outputs of the derivation which produced them.
|
||||
> This is an optimization that means if the wrong output hash is specified in a derivation, and then the derivation is recreated with the right output hash, derivation does not need to be rebuilt --- avoiding downloading potentially large amounts of data twice.
|
||||
> This is an optimization that means if the wrong output hash is specified in a derivation, and then the derivation is recreated with the right output hash, derivation does not need to be rebuilt — avoiding downloading potentially large amounts of data twice.
|
||||
> This optimisation prefigures the design above:
|
||||
> If the output hash assertion was removed outside the derivation itself, Nix could additionally not only register that outputted store object like today, but could also make note that derivation did in fact successfully download some data.
|
||||
For example, for the "fetch URL" example above, making such a note is tantamount to recording what data is available at the time of download at the given URL.
|
||||
|
||||
@@ -43,7 +43,7 @@ In particular, the specification decides:
|
||||
|
||||
- if the content is content-addressed, how is it content addressed
|
||||
|
||||
- if the content is content-addressed, [what is its content address](./content-address.md#fixed-content-addressing) (and thus what is its [store path])
|
||||
- if the content is content-addressed, [what is its content address](./content-address.md#fixed) (and thus what is its [store path])
|
||||
|
||||
## Types of derivations
|
||||
|
||||
|
||||
@@ -6,26 +6,221 @@
|
||||
That is to say, an input-addressed output's store path is a function not of the output itself, but of the derivation that produced it.
|
||||
Even if two store paths have the same contents, if they are produced in different ways, and one is input-addressed, then they will have different store paths, and thus guaranteed to not be the same store object.
|
||||
|
||||
<!---
|
||||
## Modulo content addressed derivation outputs {#hash-quotient-drv}
|
||||
|
||||
### Modulo fixed-output derivations
|
||||
A naive implementation of an output hash computation for input-addressed outputs would be to hash the derivation hash and output together.
|
||||
This clearly has the uniqueness properties we want for input-addressed outputs, but suffers from an inefficiency.
|
||||
Specifically, new builds would be required whenever a change is made to a fixed-output derivation, despite having provably no differences in the inputs to the new derivation compared to what it used to be.
|
||||
Concretely, this would cause a "mass rebuild" whenever any fetching detail changes, including mirror lists, certificate authority certificates, etc.
|
||||
|
||||
**TODO hash derivation modulo.**
|
||||
To solve this problem, we compute output hashes differently, so that certain output hashes become identical.
|
||||
We call this concept quotient hashing, in reference to quotient types or sets.
|
||||
|
||||
So how do we compute the hash part of the output path of a derivation?
|
||||
This is done by the function `hashDrv`, shown in Figure 5.10.
|
||||
It distinguishes between two cases.
|
||||
If the derivation is a fixed-output derivation, then it computes a hash over just the `outputHash` attributes.
|
||||
So how do we compute the hash part of the output paths of an input-addressed derivation?
|
||||
This is done by the function `hashQuotientDerivation`, shown below.
|
||||
|
||||
If the derivation is not a fixed-output derivation, we replace each element in the derivation’s inputDrvs with the result of a call to `hashDrv` for that element.
|
||||
(The derivation at each store path in `inputDrvs` is converted from its on-disk ATerm representation back to a `StoreDrv` by the function `parseDrv`.) In essence, `hashDrv` partitions store derivations into equivalence classes, and for hashing purpose it replaces each store path in a derivation graph with its equivalence class.
|
||||
First, a word on inputs.
|
||||
`hashQuotientDerivation` is only defined on derivations whose [inputs](@docroot@/store/derivation/index.md#inputs) take the first-order form:
|
||||
```typescript
|
||||
type ConstantPath = {
|
||||
path: StorePath;
|
||||
};
|
||||
|
||||
The recursion in Figure 5.10 is inefficient:
|
||||
it will call itself once for each path by which a subderivation can be reached, i.e., `O(V k)` times for a derivation graph with `V` derivations and with out-degree of at most `k`.
|
||||
In the actual implementation, memoisation is used to reduce this to `O(V + E)` complexity for a graph with E edges.
|
||||
type FirstOrderOutputPath = {
|
||||
drvPath: StorePath;
|
||||
output: OutputName;
|
||||
};
|
||||
|
||||
-->
|
||||
type FirstOrderDerivingPath = ConstantPath | FirstOrderOutputPath;
|
||||
|
||||
type Inputs = Set<FirstOrderDerivingPath>;
|
||||
```
|
||||
|
||||
For the algorithm below, we adopt a derivation where the two types of (first order) derived paths are partitioned into two sets, as follows:
|
||||
```typescript
|
||||
type Derivation = {
|
||||
// inputs: Set<FirstOrderDerivingPath>; // replaced
|
||||
inputSrcs: Set<ConstantPath>; // new instead
|
||||
inputDrvOutputs: Set<FirstOrderOutputPath>; // new instead
|
||||
// ...other fields...
|
||||
};
|
||||
```
|
||||
|
||||
In the [currently-experimental][xp-feature-dynamic-derivations] higher-order case where outputs of outputs are allowed as [deriving paths][deriving-path] and thus derivation inputs, derivations using that generalization are not valid arguments to this function.
|
||||
Those derivations must be (partially) [resolved](@docroot@/store/resolution.md) enough first, to the point where no such higher-order inputs remain.
|
||||
Then, and only then, can input addresses be assigned.
|
||||
|
||||
```
|
||||
function hashQuotientDerivation(drv) -> Hash:
|
||||
assert(drv.outputs are input-addressed)
|
||||
drv′ ← drv with {
|
||||
inputDrvOutputs = ⋃(
|
||||
assert(drvPath is store path)
|
||||
case hashOutputsOrQuotientDerivation(readDrv(drvPath)) of
|
||||
drvHash : Hash →
|
||||
(drvHash.toBase16(), output)
|
||||
outputHashes : Map[String, Hash] →
|
||||
(outputHashes[output].toBase16(), "out")
|
||||
| (drvPath, output) ∈ drv.inputDrvOutputs
|
||||
)
|
||||
}
|
||||
return hashSHA256(printDrv(drv′))
|
||||
|
||||
function hashOutputsOrQuotientDerivation(drv) -> Map[String, Hash] | Hash:
|
||||
if drv.outputs are content-addressed:
|
||||
return {
|
||||
outputName ↦ hashSHA256(
|
||||
"fixed:out:" + ca.printMethodAlgo() +
|
||||
":" + ca.hash.toBase16() +
|
||||
":" + ca.makeFixedOutputPath(drv.name, outputName))
|
||||
| (outputName ↦ output) ∈ drv.outputs
|
||||
, ca = output.contentAddress // or get from build trace if floating
|
||||
}
|
||||
else: // drv.outputs are input-addressed
|
||||
return hashQuotientDerivation(drv)
|
||||
```
|
||||
|
||||
### `hashQuotientDerivation`
|
||||
|
||||
We replace each element in the derivation's `inputDrvOutputs` using data from a call to `hashOutputsOrQuotientDerivation` on the `drvPath` of that element.
|
||||
When `hashOutputsOrQuotientDerivation` returns a single drv hash (because the input derivation in question is input-addressing), we simply swap out the `drvPath` for that hash, and keep the same output name.
|
||||
When `hashOutputsOrQuotientDerivation` returns a map of content addresses per-output, we look up the output in question, and pair it with the output name `out`.
|
||||
|
||||
The resulting pseudo-derivation (with hashes instead of store paths in `inputDrvs`) is then printed (in the ["ATerm" format](@docroot@/protocols/derivation-aterm.md)) and hashed, and this becomes the hash of the "quotient derivation".
|
||||
|
||||
When calculating output hashes, `hashQuotientDerivation` is called on an almost-complete input-addressing derivation, which is just missing its input-addressed outputs paths.
|
||||
The derivation hash is then used to calculate output paths for each output.
|
||||
<!-- TODO describe how this is done. -->
|
||||
Those output paths can then be substituted into the almost-complete input-addressed derivation to complete it.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> There may be an unintentional deviation from specification currently implemented in the `(outputHashes[output].toBase16(), "out")` case.
|
||||
> This is not fatal because the deviation would only apply for content-addressing derivations with more than one output, and that only occurs in the floating case, which is [experimental][xp-feature-ca-derivations].
|
||||
> Once this bug is fixed, this note will be removed.
|
||||
|
||||
### `hashOutputsOrQuotientDerivation`
|
||||
|
||||
How does `hashOutputsOrQuotientDerivation` in turn work?
|
||||
It consists of two main cases, based on whether the outputs of the derivation are to be input-addressed or content-addressed.
|
||||
|
||||
#### Input-addressed outputs case
|
||||
|
||||
In the input-addressed case, it just calls `hashQuotientDerivation`, and returns that derivation hash.
|
||||
This makes `hashQuotientDerivation` and `hashOutputsOrQuotientDerivation` mutually-recursive.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> In this case, `hashQuotientDerivation` is being called on a *complete* input-addressing derivation that already has its output paths calculated.
|
||||
> The `inputDrvs` substitution takes place anyways.
|
||||
|
||||
#### Content-addressed outputs case
|
||||
|
||||
If the outputs are [content-addressed](./content-address.md), then it computes a hash for each output derived from the content-address of that output.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> In the [fixed](./content-address.md#fixed) content-addressing case, the outputs' content addresses are statically specified in advance, so this always just works.
|
||||
> (The fixed case is what the pseudo-code shows.)
|
||||
>
|
||||
> In the [floating](./content-address.md#floating) case, the content addresses are not specified in advance.
|
||||
> This is what the "or get from [build trace](@docroot@/store/build-trace.md) if floating" comment refers to.
|
||||
> In this case, the algorithm is *stuck* until the input in question is built, and we know what the actual contents of the output in question is.
|
||||
>
|
||||
> That is OK however, because there is no problem with delaying the assigning of input addresses (which, remember, is what `hashQuotientDerivation` is ultimately for) until all inputs are known.
|
||||
|
||||
### Performance
|
||||
|
||||
The recursion in the algorithm is potentially inefficient:
|
||||
it could call itself once for each path by which a subderivation can be reached, i.e., `O(V^k)` times for a derivation graph with `V` derivations and with out-degree of at most `k`.
|
||||
In the actual implementation, [memoisation](https://en.wikipedia.org/wiki/Memoization) is used to reduce this cost to be proportional to the total number of `inputDrvOutputs` encountered.
|
||||
|
||||
### Semantic properties
|
||||
|
||||
*See [this chapter's appendix](@docroot@/store/math-notation.md) on grammar and metavariable conventions.*
|
||||
|
||||
In essence, `hashQuotientDerivation` partitions input-addressing derivations into equivalence classes: every derivation in that equivalence class is mapped to the same derivation hash.
|
||||
We can characterize this equivalence relation directly, by working bottom up.
|
||||
|
||||
We start by defining an equivalence relation on first-order output deriving paths that refer content-addressed derivation outputs. Two such paths are equivalent if they refer to the same store object:
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$d\_1$ is content-addressing}
|
||||
\\AxiomC{$d\_2$ is content-addressing}
|
||||
\\AxiomC{$
|
||||
{}^\*(\text{path}(d\_1), o\_1)
|
||||
\=
|
||||
{}^\*(\text{path}(d\_2), o\_2)
|
||||
$}
|
||||
\\TrinaryInfC{$(\text{path}(d\_1), o\_1) \\,\\sim_{\\mathrm{CA}}\\, (d\_2, o\_2)$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
where \\({}^*(s, o)\\) denotes the store object that the output deriving path refers to.
|
||||
|
||||
We will also need the following construction to lift any equivalence relation on \\(X\\) to an equivalence relation on (finite) sets of \\(X\\) (in short, \\(\\mathcal{P}(X)\\)):
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$\\forall a \\in A. \\exists b \\in B. a \\,\\sim\_X\\, b$}
|
||||
\\AxiomC{$\\forall b \\in B. \\exists a \\in A. b \\,\\sim\_X\\, a$}
|
||||
\\BinaryInfC{$A \\,\\sim_{\\mathcal{P}(X)}\\, B$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
Now we can define the equivalence relation \\(\\sim_\\mathrm{IA}\\) on input-addressed derivation outputs. Two input-addressed outputs are equivalent if their derivations are equivalent (via the yet-to-be-defined \\(\\sim_{\\mathrm{IADrv}}\\) relation) and their output names are the same:
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$d\_1$ is input-addressing}
|
||||
\\AxiomC{$d\_2$ is input-addressing}
|
||||
\\AxiomC{$d\_1 \\,\\sim_{\\mathrm{IADrv}}\\, d\_2$}
|
||||
\\AxiomC{$o\_1 = o\_2$}
|
||||
\\QuaternaryInfC{$(\text{path}(d\_1), o\_1) \\,\\sim_{\\mathrm{IA}}\\, (\text{path}(d\_2), o\_2)$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
And now we can define \\(\\sim_{\\mathrm{IADrv}}\\).
|
||||
Two input-addressed derivations are equivalent if their content-addressed inputs are equivalent, their input-addressed inputs are also equivalent, and they are otherwise equal:
|
||||
|
||||
<!-- cheating a bit with the semantics to get a good layout that fits on the page -->
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\alwaysNoLine
|
||||
\\AxiomC{$
|
||||
\\mathrm{caInputs}(d\_1)
|
||||
\\,\\sim_{\\mathcal{P}(\\mathrm{CA})}\\,
|
||||
\\mathrm{caInputs}(d\_2)
|
||||
$}
|
||||
\\AxiomC{$
|
||||
\\mathrm{iaInputs}(d\_1)
|
||||
\\,\\sim_{\\mathcal{P}(\\mathrm{IA})}\\,
|
||||
\\mathrm{iaInputs}(d\_2)
|
||||
$}
|
||||
\\BinaryInfC{$
|
||||
d\_1\left[\\mathrm{inputDrvOutputs} := \\{\\}\right]
|
||||
\=
|
||||
d\_2\left[\\mathrm{inputDrvOutputs} := \\{\\}\right]
|
||||
$}
|
||||
\\alwaysSingleLine
|
||||
\\UnaryInfC{$d\_1 \\,\\sim_{\\mathrm{IADrv}}\\, d\_2$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
where \\(\\mathrm{caInputs}(d)\\) returns the content-addressed inputs of \\(d\\) and \\(\\mathrm{iaInputs}(d)\\) returns the input-addressed inputs.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> An astute reader might notice that that nowhere does `inputSrcs` enter into these definitions.
|
||||
> That means that replacing an input derivation with its outputs directly added to `inputSrcs` always results in a derivation in a different equivalence class, despite the resulting input closure (as would be mounted in the store at build time) being the same.
|
||||
> [Issue #9259](https://github.com/NixOS/nix/issues/9259) is about creating a coarser equivalence relation to address this.
|
||||
>
|
||||
> \\(\\sim_\mathrm{Drv}\\) from [derivation resolution](@docroot@/store/resolution.md) is such an equivalence relation.
|
||||
> It is coarser than this one: any two derivations which are "'hash quotient derivation'-equivalent" (\\(\\sim_\mathrm{IADrv}\\)) are also "resolution-equivalent" (\\(\\sim_\mathrm{Drv}\\)).
|
||||
> It also relates derivations whose `inputDrvOutputs` have been rewritten into `inputSrcs`.
|
||||
|
||||
[deriving-path]: @docroot@/store/derivation/index.md#deriving-path
|
||||
[xp-feature-dynamic-derivations]: @docroot@/development/experimental-features.md#xp-feature-dynamic-derivations
|
||||
[xp-feature-ca-derivations]: @docroot@/development/experimental-features.md#xp-feature-ca-derivations
|
||||
[xp-feature-git-hashing]: @docroot@/development/experimental-features.md#xp-feature-git-hashing
|
||||
[xp-feature-impure-derivations]: @docroot@/development/experimental-features.md#xp-feature-impure-derivations
|
||||
|
||||
@@ -46,7 +46,7 @@ be many different serialisations.
|
||||
For these reasons, Nix has its very own archive format—the Nix Archive (NAR) format,
|
||||
which is carefully designed to avoid the problems described above.
|
||||
|
||||
The exact specification of the Nix Archive format is in [specified here](../../protocols/nix-archive.md).
|
||||
The exact specification of the Nix Archive format is in [specified here](../../protocols/nix-archive/index.md).
|
||||
|
||||
## Content addressing File System Objects beyond a single serialisation pass
|
||||
|
||||
|
||||
16
doc/manual/source/store/math-notation.md
Normal file
16
doc/manual/source/store/math-notation.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Appendix: Math notation
|
||||
|
||||
A few times in this manual, formal "proof trees" are used for [natural deduction](https://en.wikipedia.org/wiki/Natural_deduction)-style definition of various [relations](https://en.wikipedia.org/wiki/Relation_(mathematics)).
|
||||
|
||||
The following grammar and assignment of metavariables to syntactic categories is used in these sections.
|
||||
|
||||
\\begin{align}
|
||||
s, t &\in \text{store-path} \\\\
|
||||
o &\in \text{output-name} \\\\
|
||||
i, p &\in \text{deriving-path} \\\\
|
||||
d &\in \text{derivation}
|
||||
\\end{align}
|
||||
|
||||
\\begin{align}
|
||||
\text{deriving-path} \quad p &::= s \mid (p, o)
|
||||
\\end{align}
|
||||
219
doc/manual/source/store/resolution.md
Normal file
219
doc/manual/source/store/resolution.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# Derivation Resolution
|
||||
|
||||
*See [this chapter's appendix](@docroot@/store/math-notation.md) on grammar and metavariable conventions.*
|
||||
|
||||
To *resolve* a derivation is to replace its [inputs] with the simplest inputs — plain store paths — that denote the same store objects.
|
||||
|
||||
Derivations that only have store paths as inputs are likewise called *resolved derivations*.
|
||||
(They are called that whether they are in fact the output of derivation resolution, or just made that way without non-store-path inputs to begin with.)
|
||||
|
||||
## Input Content Equivalence of Derivations
|
||||
|
||||
[Deriving paths][deriving-path] intentionally make it possible to refer to the same [store object] in multiple ways.
|
||||
This is a consequence of content-addressing, since different derivations can produce the same outputs, and the same data can also be manually added to the store.
|
||||
This is also a consequence even of input-addressing, as an output can be referred to by derivation and output name, or directly by its [computed](./derivation/outputs/input-address.md) store path.
|
||||
Since dereferencing deriving paths is thus not injective, it induces an equivalence relation on deriving paths.
|
||||
|
||||
Let's call this equivalence relation \\(\\sim\\), where \\(p_1 \\sim p_2\\) means that deriving paths \\(p_1\\) and \\(p_2\\) refer to the same store object.
|
||||
|
||||
**Content Equivalence**: Two deriving paths are equivalent if they refer to the same store object:
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{${}^*p_1 = {}^*p_2$}
|
||||
\\UnaryInfC{$p_1 \\,\\sim_\\mathrm{DP}\\, p_2$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
where \\({}^\*p\\) denotes the store object that deriving path \\(p\\) refers to.
|
||||
|
||||
This also induces an equivalence relation on sets of deriving paths:
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$\\{ {}^*p | p \\in P_1 \\} = \\{ {}^*p | p \\in P_2 \\}$}
|
||||
\\UnaryInfC{$P_1 \\,\\sim_{\\mathcal{P}(\\mathrm{DP})}\\, P_2$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
**Input Content Equivalence**: This, in turn, induces an equivalence relation on derivations: two derivations are equivalent if their inputs are equivalent, and they are otherwise equal:
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$\\mathrm{inputs}(d_1) \\,\\sim_{\\mathcal{P}(\\mathrm{DP})}\\, \\mathrm{inputs}(d_2)$}
|
||||
\\AxiomC{$
|
||||
d\_1\left[\\mathrm{inputs} := \\{\\}\right]
|
||||
\=
|
||||
d\_2\left[\\mathrm{inputs} := \\{\\}\right]
|
||||
$}
|
||||
\\BinaryInfC{$d_1 \\,\\sim_\\mathrm{Drv}\\, d_2$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
Derivation resolution always maps derivations to input-content-equivalent derivations.
|
||||
|
||||
## Resolution relation
|
||||
|
||||
Dereferencing a derived path — \\({}^\*p\\) above — was just introduced as a black box.
|
||||
But actually it is a multi-step process of looking up build results in the [build trace] that itself depends on resolving the lookup keys.
|
||||
Resolution is thus a recursive multi-step process that is worth diagramming formally.
|
||||
|
||||
We can do this with a small-step binary transition relation; let's call it \\(\rightsquigarrow\\).
|
||||
We can then conclude dereferenced equality like this:
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$p\_1 \\rightsquigarrow^* p$}
|
||||
\\AxiomC{$p\_2 \\rightsquigarrow^* p$}
|
||||
\\BinaryInfC{${}^*p\_1 = {}^*p\_2$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
I.e. by showing that both original items resolve (over 0 or more small steps, hence the \\({}^*\\)) to the same exact item.
|
||||
|
||||
With this motivation, let's now formalize a [small-step](https://en.wikipedia.org/wiki/Operational_semantics#Small-step_semantics) system of reduction rules for resolution.
|
||||
|
||||
### Formal rules
|
||||
|
||||
### \\(\text{resolved}\\) unary relation
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$s \in \text{store-path}$}
|
||||
\\UnaryInfC{$s$ resolved}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$\forall i \in \mathrm{inputs}(d). i \text{ resolved}$}
|
||||
\\UnaryInfC{$d$ resolved}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
### \\(\rightsquigarrow\\) binary relation
|
||||
|
||||
> **Remark**
|
||||
>
|
||||
> Actually, to be completely formal we would need to keep track of the build trace we are choosing to resolve against.
|
||||
>
|
||||
> We could do that by making \\(\rightsquigarrow\\) a ternary relation, which would pass the build trace to itself until it finally uses it in that one rule.
|
||||
> This would add clutter more than insight, so we didn't bother to write it.
|
||||
>
|
||||
> There are other options too, like saying the whole reduction rule system is parameterized on the build trace, essentially [currying](https://en.wikipedia.org/wiki/Currying) the ternary \\(\rightsquigarrow\\) into a function from build traces to the binary relation written above.
|
||||
|
||||
#### Core build trace lookup rule
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$s \in \text{store-path}$}
|
||||
\\AxiomC{${}^*s \in \text{derivation}$}
|
||||
\\AxiomC{${}^*s$ resolved}
|
||||
\\AxiomC{$\mathrm{build\text{-}trace}[s][o] = t$}
|
||||
\\QuaternaryInfC{$(s, o) \rightsquigarrow t$}
|
||||
\\RightLabel{\\scriptsize output path resolution}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
#### Inductive rules
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$i \\rightsquigarrow i'$}
|
||||
\\AxiomC{$i \\in \\mathrm{inputs}(d)$}
|
||||
\\BinaryInfC{$d \\rightsquigarrow d[i \\mapsto i']$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$d \\rightsquigarrow d'$}
|
||||
\\UnaryInfC{$(\\mathrm{path}(d), o) \\rightsquigarrow (\\mathrm{path}(d'), o)$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
\\[
|
||||
\\begin{prooftree}
|
||||
\\AxiomC{$p \\rightsquigarrow p'$}
|
||||
\\UnaryInfC{$(p, o) \\rightsquigarrow (p', o)$}
|
||||
\\end{prooftree}
|
||||
\\]
|
||||
|
||||
### Properties
|
||||
|
||||
Like all well-behaved evaluation relations, partial resolution is [*confluent*](https://en.wikipedia.org/wiki/Confluence_(abstract_rewriting)).
|
||||
Also, if we take the symmetric closure of \\(\\rightsquigarrow^\*\\), we end up with the equivalence relations of the previous section.
|
||||
Resolution respects content equivalence for deriving paths, and input content equivalence for derivations.
|
||||
|
||||
> **Remark**
|
||||
>
|
||||
> We chose to define from scratch an "resolved" unary relation explicitly above.
|
||||
> But it can also be defined as the normal forms of the \\(\\rightsquigarrow^\*\\) relation:
|
||||
>
|
||||
> \\[ a \text{ resolved} \Leftrightarrow \forall b. b \rightsquigarrow^* a \Rightarrow b = a\\]
|
||||
>
|
||||
> In prose, resolved terms are terms which \\(\\rightsquigarrow^\*\\) only relates on the left side to the same term on the right side; they are the terms which can be resolved no further.
|
||||
|
||||
## Partial versus Complete Resolution
|
||||
|
||||
Similar to evaluation, we can also speak of *partial* versus *complete* derivation resolution.
|
||||
Partial derivation resolution is what we've actually formalized above with \\(\\rightsquigarrow^\*\\).
|
||||
Complete resolution is resolution ending in a resolved term (deriving path or derivation).
|
||||
(Which is a normal form of the relation, per the remark above.)
|
||||
|
||||
With partial resolution, a derivation is related to equivalent derivations with the same or simpler inputs, but not all those inputs will be plain store paths.
|
||||
This is useful when the input refers to a floating content addressed output we have not yet built — we don't know what (content-address) store path will used for that derivation, so we are "stuck" trying to resolve the deriving path in question.
|
||||
(In the above formalization, this happens when the build trace is missing the keys we wish to look up in it.)
|
||||
|
||||
Complete resolution is a *functional* relation, i.e. values on the left are uniquely related with values on the right.
|
||||
It is not however, a *total* relation (in general, assuming arbitrary build traces).
|
||||
This is discussed in the next section.
|
||||
|
||||
## Termination
|
||||
|
||||
For static derivations graphs, complete resolution is indeed total, because it always terminates for all inputs.
|
||||
(A relation that is both total and functional is a function.)
|
||||
|
||||
For [dynamic][xp-feature-dynamic-derivations] derivation graphs, however, this is not the case — resolution is not guaranteed to terminate.
|
||||
The issue isn't rewriting deriving paths themselves:
|
||||
a single rewrite to normalize an output deriving path to a constant one always exists, and always proceeds in one step.
|
||||
The issue is that dynamic derivations (i.e. those that are filled-in the graph by a previous resolution) may have more transitive dependencies than the original derivation.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> Suppose we have this deriving path
|
||||
> ```json
|
||||
> {
|
||||
> "drvPath": {
|
||||
> "drvPath": "...-foo.drv",
|
||||
> "output": "bar.drv"
|
||||
> },
|
||||
> "output": "baz"
|
||||
> }
|
||||
> ```
|
||||
> and derivation `foo` is already resolved.
|
||||
> When we resolve deriving path we'll end up with something like.
|
||||
> ```json
|
||||
> {
|
||||
> "drvPath": "...-foo-bar.drv",
|
||||
> "output": "baz"
|
||||
> }
|
||||
> ```
|
||||
> So far is just an atomic single rewrite, with no termination issues.
|
||||
> But the derivation `foo-bar` may have its *own* dynamic derivation inputs.
|
||||
> Resolution must resolve that derivation first before the above deriving path can finally be normalized to a plain `...-foo-bar-baz` store path.
|
||||
|
||||
The important thing to notice is that while "build trace" *keys* must be resolved.
|
||||
The *value* those keys are mapped to have no such constraints.
|
||||
An arbitrary store object has no notion of being resolved or not.
|
||||
But, an arbitrary store object can be read back as a derivation (as will in fact be done in case for dynamic derivations / nested output deriving paths).
|
||||
And those derivations need *not* be resolved.
|
||||
|
||||
It is those dynamic non-resolved derivations which are the source of non-termination.
|
||||
By the same token, they are also the reason why dynamic derivations offer greater expressive power.
|
||||
|
||||
[store object]: @docroot@/store/store-object.md
|
||||
[inputs]: @docroot@/store/derivation/index.md#inputs
|
||||
[build trace]: @docroot@/store/build-trace.md
|
||||
[deriving-path]: @docroot@/store/derivation/index.md#deriving-path
|
||||
[xp-feature-dynamic-derivations]: @docroot@/development/experimental-features.md#xp-feature-dynamic-derivations
|
||||
@@ -1,7 +1,7 @@
|
||||
# Content-Addressing Store Objects
|
||||
|
||||
Just [like][fso-ca] [File System Objects][File System Object],
|
||||
[Store Objects][Store Object] can also be [content-addressed](@docroot@/glossary.md#gloss-content-addressed),
|
||||
[Store Objects][Store Object] can also be [content-addressed](@docroot@/glossary.md#gloss-content-address),
|
||||
unless they are [input-addressed](@docroot@/glossary.md#gloss-input-addressed-store-object).
|
||||
|
||||
For store objects, the content address we produce will take the form of a [Store Path] rather than regular hash.
|
||||
@@ -107,7 +107,7 @@ References (to other store objects and self-references alike) are supported so l
|
||||
>
|
||||
> This method is part of the [`git-hashing`][xp-feature-git-hashing] experimental feature.
|
||||
|
||||
This uses the corresponding [Git](../file-system-object/content-address.md#serial-git) method of file system object content addressing.
|
||||
This uses the corresponding [Git](../file-system-object/content-address.md#git) method of file system object content addressing.
|
||||
|
||||
References are not supported.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
>
|
||||
> A rendered store path
|
||||
|
||||
Nix implements references to [store objects](./index.md#store-object) as *store paths*.
|
||||
Nix implements references to [store objects](./store-object.md) as *store paths*.
|
||||
|
||||
Think of a store path as an [opaque], [unique identifier]:
|
||||
The only way to obtain store path is by adding or building store objects.
|
||||
|
||||
15
doc/manual/theme/head.hbs
Normal file
15
doc/manual/theme/head.hbs
Normal file
@@ -0,0 +1,15 @@
|
||||
<script>
|
||||
MathJax = {
|
||||
loader: {load: ['[tex]/bussproofs']},
|
||||
tex: {
|
||||
packages: {'[+]': ['bussproofs']},
|
||||
// Doesn't seem to work in mathjax 3
|
||||
//formatError: function(jax, error) {
|
||||
// console.log(`TeX error in "${jax.latex}": ${error.message}`);
|
||||
// return jax.formatError(error);
|
||||
//}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<!-- Load a newer versino of MathJax than mdbook does by default, and which in particular has working relative paths for the "bussproofs" extension. -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.0.1/es5/tex-mml-chtml.js"></script>
|
||||
@@ -10,7 +10,7 @@
|
||||
tag ? "latest",
|
||||
bundleNixpkgs ? true,
|
||||
channelName ? "nixpkgs",
|
||||
channelURL ? "https://nixos.org/channels/nixpkgs-unstable",
|
||||
channelURL ? "https://channels.nixos.org/nixpkgs-unstable",
|
||||
extraPkgs ? [ ],
|
||||
maxLayers ? 70,
|
||||
nixConf ? { },
|
||||
|
||||
8
flake.lock
generated
8
flake.lock
generated
@@ -63,16 +63,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1756178832,
|
||||
"narHash": "sha256-O2CIn7HjZwEGqBrwu9EU76zlmA5dbmna7jL1XUmAId8=",
|
||||
"lastModified": 1761597516,
|
||||
"narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d98ce345cdab58477ca61855540999c86577d19d",
|
||||
"rev": "daf6dc47aa4b44791372d6139ab7b25269184d55",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-25.05-small",
|
||||
"ref": "nixos-25.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
12
flake.nix
12
flake.nix
@@ -1,7 +1,7 @@
|
||||
{
|
||||
description = "The purely functional package manager";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05-small";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||
|
||||
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||
inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446";
|
||||
@@ -417,6 +417,10 @@
|
||||
supportsCross = false;
|
||||
};
|
||||
|
||||
"nix-kaitai-struct-checks" = {
|
||||
supportsCross = false;
|
||||
};
|
||||
|
||||
"nix-perl-bindings" = {
|
||||
supportsCross = false;
|
||||
};
|
||||
@@ -481,10 +485,10 @@
|
||||
open-manual = {
|
||||
type = "app";
|
||||
program = "${pkgs.writeShellScript "open-nix-manual" ''
|
||||
manual_path="${self.packages.${system}.nix-manual}/share/doc/nix/manual/index.html"
|
||||
if ! ${opener} "$manual_path"; then
|
||||
path="${self.packages.${system}.nix-manual.site}/index.html"
|
||||
if ! ${opener} "$path"; then
|
||||
echo "Failed to open manual with ${opener}. Manual is located at:"
|
||||
echo "$manual_path"
|
||||
echo "$path"
|
||||
fi
|
||||
''}";
|
||||
meta.description = "Open the Nix manual in your browser";
|
||||
|
||||
@@ -42,6 +42,26 @@ if cxx.get_id() == 'clang'
|
||||
add_project_arguments('-fpch-instantiate-templates', language : 'cpp')
|
||||
endif
|
||||
|
||||
# Detect if we're using libstdc++ (GCC's standard library)
|
||||
# libstdc++ uses Intel TBB as backend for C++17 parallel algorithms when <execution> is included.
|
||||
# boost::concurrent_flat_map includes <execution>, which would require linking against TBB.
|
||||
# Since we don't actually use parallel algorithms, disable the TBB backend to avoid the dependency.
|
||||
# TBB is a dependency of blake3 and leaking into our build environment.
|
||||
is_using_libstdcxx = cxx.compiles(
|
||||
'''
|
||||
#include <ciso646>
|
||||
#ifndef __GLIBCXX__
|
||||
#error "not libstdc++"
|
||||
#endif
|
||||
int main() { return 0; }
|
||||
''',
|
||||
name : 'using libstdc++',
|
||||
)
|
||||
|
||||
if is_using_libstdcxx
|
||||
add_project_arguments('-D_GLIBCXX_USE_TBB_PAR_BACKEND=0', language : 'cpp')
|
||||
endif
|
||||
|
||||
# Darwin ld doesn't like "X.Y.ZpreABCD+W"
|
||||
nix_soversion = meson.project_version().split('+')[0].split('pre')[0]
|
||||
|
||||
|
||||
@@ -443,6 +443,11 @@ in
|
||||
*/
|
||||
nix-json-schema-checks = callPackage ../src/json-schema-checks/package.nix { };
|
||||
|
||||
/**
|
||||
Kaitai struct schema validation checks
|
||||
*/
|
||||
nix-kaitai-struct-checks = callPackage ../src/kaitai-struct-checks/package.nix { };
|
||||
|
||||
nix-perl-bindings = callPackage ../src/perl/package.nix { };
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,4 +74,14 @@ 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/'
|
||||
'';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ let
|
||||
"nix-cli"
|
||||
"nix-functional-tests"
|
||||
"nix-json-schema-checks"
|
||||
"nix-kaitai-struct-checks"
|
||||
]
|
||||
++ lib.optionals enableBindings [
|
||||
"nix-perl-bindings"
|
||||
|
||||
@@ -714,7 +714,7 @@ EOF
|
||||
|
||||
place_channel_configuration() {
|
||||
if [ -z "${NIX_INSTALLER_NO_CHANNEL_ADD:-}" ]; then
|
||||
echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
|
||||
echo "https://channels.nixos.org/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
|
||||
_sudo "to set up the default system channel (part 1)" \
|
||||
install -m 0644 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
|
||||
fi
|
||||
|
||||
@@ -213,7 +213,7 @@ fi
|
||||
# Subscribe the user to the Nixpkgs channel and fetch it.
|
||||
if [ -z "$NIX_INSTALLER_NO_CHANNEL_ADD" ]; then
|
||||
if ! "$nix/bin/nix-channel" --list | grep -q "^nixpkgs "; then
|
||||
"$nix/bin/nix-channel" --add https://nixos.org/channels/nixpkgs-unstable
|
||||
"$nix/bin/nix-channel" --add https://channels.nixos.org/nixpkgs-unstable
|
||||
fi
|
||||
if [ -z "$_NIX_INSTALLER_TEST" ]; then
|
||||
if ! "$nix/bin/nix-channel" --update nixpkgs; then
|
||||
|
||||
1
src/json-schema-checks/build-result
Symbolic link
1
src/json-schema-checks/build-result
Symbolic link
@@ -0,0 +1 @@
|
||||
../../src/libstore-tests/data/build-result
|
||||
1
src/json-schema-checks/build-trace-entry
Symbolic link
1
src/json-schema-checks/build-trace-entry
Symbolic link
@@ -0,0 +1 @@
|
||||
../../src/libstore-tests/data/realisation
|
||||
@@ -45,9 +45,32 @@ schemas = [
|
||||
'simple.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'deriving-path',
|
||||
'schema' : schema_dir / 'deriving-path-v1.yaml',
|
||||
'files' : [
|
||||
'single_opaque.json',
|
||||
'single_built.json',
|
||||
'single_built_built.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'build-trace-entry',
|
||||
'schema' : schema_dir / 'build-trace-entry-v1.yaml',
|
||||
'files' : [
|
||||
'simple.json',
|
||||
'with-dependent-realisations.json',
|
||||
'with-signature.json',
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
# Derivation and Derivation output
|
||||
schemas += [
|
||||
# Match overall
|
||||
{
|
||||
'stem' : 'derivation',
|
||||
'schema' : schema_dir / 'derivation-v3.yaml',
|
||||
'schema' : schema_dir / 'derivation-v4.yaml',
|
||||
'files' : [
|
||||
'dyn-dep-derivation.json',
|
||||
'simple-derivation.json',
|
||||
@@ -55,7 +78,7 @@ schemas = [
|
||||
},
|
||||
{
|
||||
'stem' : 'derivation',
|
||||
'schema' : schema_dir / 'derivation-v3.yaml#/$defs/output',
|
||||
'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/overall',
|
||||
'files' : [
|
||||
'output-caFixedFlat.json',
|
||||
'output-caFixedNAR.json',
|
||||
@@ -66,19 +89,52 @@ schemas = [
|
||||
'output-inputAddressed.json',
|
||||
],
|
||||
},
|
||||
# Match exact variant
|
||||
{
|
||||
'stem' : 'deriving-path',
|
||||
'schema' : schema_dir / 'deriving-path-v1.yaml',
|
||||
'stem' : 'derivation',
|
||||
'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/inputAddressed',
|
||||
'files' : [
|
||||
'single_opaque.json',
|
||||
'single_built.json',
|
||||
'single_built_built.json',
|
||||
'output-inputAddressed.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'derivation',
|
||||
'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/caFixed',
|
||||
'files' : [
|
||||
'output-caFixedFlat.json',
|
||||
'output-caFixedNAR.json',
|
||||
'output-caFixedText.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'derivation',
|
||||
'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/caFloating',
|
||||
'files' : [
|
||||
'output-caFloating.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'derivation',
|
||||
'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/deferred',
|
||||
'files' : [
|
||||
'output-deferred.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'derivation',
|
||||
'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/impure',
|
||||
'files' : [
|
||||
'output-impure.json',
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
# Store object info
|
||||
schemas += [
|
||||
# Match overall
|
||||
{
|
||||
'stem' : 'store-object-info',
|
||||
'schema' : schema_dir / 'store-object-info-v1.yaml',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml',
|
||||
'files' : [
|
||||
'pure.json',
|
||||
'impure.json',
|
||||
@@ -88,16 +144,25 @@ schemas = [
|
||||
},
|
||||
{
|
||||
'stem' : 'nar-info',
|
||||
'schema' : schema_dir / 'store-object-info-v1.yaml',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml',
|
||||
'files' : [
|
||||
'pure.json',
|
||||
'impure.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'build-result',
|
||||
'schema' : schema_dir / 'build-result-v1.yaml',
|
||||
'files' : [
|
||||
'success.json',
|
||||
'output-rejected.json',
|
||||
'not-deterministic.json',
|
||||
],
|
||||
},
|
||||
# Match exact variant
|
||||
{
|
||||
'stem' : 'store-object-info',
|
||||
'schema' : schema_dir / 'store-object-info-v1.yaml#/$defs/base',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
|
||||
'files' : [
|
||||
'pure.json',
|
||||
'empty_pure.json',
|
||||
@@ -105,7 +170,7 @@ schemas = [
|
||||
},
|
||||
{
|
||||
'stem' : 'store-object-info',
|
||||
'schema' : schema_dir / 'store-object-info-v1.yaml#/$defs/impure',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/impure',
|
||||
'files' : [
|
||||
'impure.json',
|
||||
'empty_impure.json',
|
||||
@@ -113,14 +178,14 @@ schemas = [
|
||||
},
|
||||
{
|
||||
'stem' : 'nar-info',
|
||||
'schema' : schema_dir / 'store-object-info-v1.yaml#/$defs/base',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
|
||||
'files' : [
|
||||
'pure.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'nar-info',
|
||||
'schema' : schema_dir / 'store-object-info-v1.yaml#/$defs/narInfo',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/narInfo',
|
||||
'files' : [
|
||||
'impure.json',
|
||||
],
|
||||
|
||||
@@ -23,10 +23,12 @@ mkMesonDerivation (finalAttrs: {
|
||||
../../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
|
||||
./.
|
||||
];
|
||||
|
||||
|
||||
1
src/kaitai-struct-checks/.version
Symbolic link
1
src/kaitai-struct-checks/.version
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.version
|
||||
77
src/kaitai-struct-checks/meson.build
Normal file
77
src/kaitai-struct-checks/meson.build
Normal file
@@ -0,0 +1,77 @@
|
||||
# Run with:
|
||||
# meson test --suite kaitai-struct
|
||||
# Run with: (without shell / configure)
|
||||
# nix build .#nix-kaitai-struct-checks
|
||||
|
||||
project(
|
||||
'nix-kaitai-struct-checks',
|
||||
'cpp',
|
||||
version : files('.version'),
|
||||
default_options : [
|
||||
'cpp_std=c++23',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
license : 'LGPL-2.1-or-later',
|
||||
)
|
||||
|
||||
kaitai_runtime_dep = dependency('kaitai-struct-cpp-stl-runtime', required : true)
|
||||
gtest_dep = dependency('gtest')
|
||||
gtest_main_dep = dependency('gtest_main', required : true)
|
||||
|
||||
# Find the Kaitai Struct compiler
|
||||
ksc = find_program('ksc', required : true)
|
||||
|
||||
kaitai_generated_srcs = custom_target(
|
||||
'kaitai-generated-sources',
|
||||
input : [ 'nar.ksy' ],
|
||||
output : [ 'nix_nar.cpp', 'nix_nar.h' ],
|
||||
command : [
|
||||
ksc,
|
||||
'@INPUT@',
|
||||
'--target', 'cpp_stl',
|
||||
'--outdir',
|
||||
meson.current_build_dir(),
|
||||
],
|
||||
)
|
||||
|
||||
nar_kaitai_lib = library(
|
||||
'nix-nar-kaitai-lib',
|
||||
kaitai_generated_srcs,
|
||||
dependencies : [ kaitai_runtime_dep ],
|
||||
install : true,
|
||||
)
|
||||
|
||||
nar_kaitai_dep = declare_dependency(
|
||||
link_with : nar_kaitai_lib,
|
||||
sources : kaitai_generated_srcs[1],
|
||||
)
|
||||
|
||||
# The nar directory is a committed symlink to the actual nars location
|
||||
nars_dir = meson.current_source_dir() / 'nars'
|
||||
|
||||
# Get all example files
|
||||
nars = [
|
||||
'dot.nar',
|
||||
]
|
||||
|
||||
test_deps = [
|
||||
nar_kaitai_dep,
|
||||
kaitai_runtime_dep,
|
||||
gtest_main_dep,
|
||||
]
|
||||
|
||||
this_exe = executable(
|
||||
meson.project_name(),
|
||||
'test-parse-nar.cc',
|
||||
dependencies : test_deps,
|
||||
)
|
||||
|
||||
test(
|
||||
meson.project_name(),
|
||||
this_exe,
|
||||
env : [ 'NIX_NARS_DIR=' + nars_dir ],
|
||||
protocol : 'gtest',
|
||||
)
|
||||
1
src/kaitai-struct-checks/nar.ksy
Symbolic link
1
src/kaitai-struct-checks/nar.ksy
Symbolic link
@@ -0,0 +1 @@
|
||||
../../doc/manual/source/protocols/nix-archive/nar.ksy
|
||||
1
src/kaitai-struct-checks/nars
Symbolic link
1
src/kaitai-struct-checks/nars
Symbolic link
@@ -0,0 +1 @@
|
||||
../libutil-tests/data/nars
|
||||
1
src/kaitai-struct-checks/nix-meson-build-support
Symbolic link
1
src/kaitai-struct-checks/nix-meson-build-support
Symbolic link
@@ -0,0 +1 @@
|
||||
../../nix-meson-build-support
|
||||
76
src/kaitai-struct-checks/package.nix
Normal file
76
src/kaitai-struct-checks/package.nix
Normal file
@@ -0,0 +1,76 @@
|
||||
# Run with: nix build .#nix-kaitai-struct-checks
|
||||
# or: `nix develop .#nix-kaitai-struct-checks` to enter a dev shell
|
||||
{
|
||||
lib,
|
||||
mkMesonDerivation,
|
||||
gtest,
|
||||
meson,
|
||||
ninja,
|
||||
pkg-config,
|
||||
kaitai-struct-compiler,
|
||||
fetchzip,
|
||||
kaitai-struct-cpp-stl-runtime,
|
||||
# Configuration Options
|
||||
version,
|
||||
}:
|
||||
let
|
||||
inherit (lib) fileset;
|
||||
in
|
||||
mkMesonDerivation (finalAttrs: {
|
||||
pname = "nix-kaitai-struct-checks";
|
||||
inherit version;
|
||||
|
||||
workDir = ./.;
|
||||
fileset = lib.fileset.unions [
|
||||
../../nix-meson-build-support
|
||||
./nix-meson-build-support
|
||||
./.version
|
||||
../../.version
|
||||
../../doc/manual/source/protocols/nix-archive/nar.ksy
|
||||
./nars
|
||||
../../src/libutil-tests/data
|
||||
./meson.build
|
||||
./nar.ksy
|
||||
(fileset.fileFilter (file: file.hasExt "cc") ./.)
|
||||
(fileset.fileFilter (file: file.hasExt "hh") ./.)
|
||||
];
|
||||
|
||||
outputs = [ "out" ];
|
||||
|
||||
passthru.externalNativeBuildInputs = [
|
||||
# This can go away when we bump up to 25.11
|
||||
(kaitai-struct-compiler.overrideAttrs (finalAttrs: {
|
||||
version = "0.11";
|
||||
src = fetchzip {
|
||||
url = "https://github.com/kaitai-io/kaitai_struct_compiler/releases/download/${version}/kaitai-struct-compiler-${version}.zip";
|
||||
sha256 = "sha256-j9TEilijqgIiD0GbJfGKkU1FLio9aTopIi1v8QT1b+A=";
|
||||
};
|
||||
}))
|
||||
];
|
||||
|
||||
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" ];
|
||||
|
||||
postInstall = ''
|
||||
touch $out
|
||||
'';
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.all;
|
||||
};
|
||||
})
|
||||
48
src/kaitai-struct-checks/test-parse-nar.cc
Normal file
48
src/kaitai-struct-checks/test-parse-nar.cc
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <kaitai/kaitaistream.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nix_nar.h"
|
||||
|
||||
static const std::vector<std::string> NarFiles = {
|
||||
"empty.nar",
|
||||
"dot.nar",
|
||||
"dotdot.nar",
|
||||
"executable-after-contents.nar",
|
||||
"invalid-tag-instead-of-contents.nar",
|
||||
"name-after-node.nar",
|
||||
"nul-character.nar",
|
||||
"slash.nar",
|
||||
};
|
||||
|
||||
class NarParseTest : public ::testing::TestWithParam<std::string>
|
||||
{};
|
||||
|
||||
TEST_P(NarParseTest, ParseSucceeds)
|
||||
{
|
||||
const auto nar_file = GetParam();
|
||||
|
||||
const char * nars_dir_env = std::getenv("NIX_NARS_DIR");
|
||||
if (nars_dir_env == nullptr) {
|
||||
FAIL() << "NIX_NARS_DIR environment variable not set.";
|
||||
}
|
||||
|
||||
const std::filesystem::path nar_file_path = std::filesystem::path(nars_dir_env) / "dot.nar";
|
||||
ASSERT_TRUE(std::filesystem::exists(nar_file_path)) << "Missing test file: " << nar_file_path;
|
||||
|
||||
std::ifstream ifs(nar_file_path, std::ifstream::binary);
|
||||
ASSERT_TRUE(ifs.is_open()) << "Failed to open file: " << nar_file;
|
||||
kaitai::kstream ks(&ifs);
|
||||
nix_nar_t nar(&ks);
|
||||
ASSERT_TRUE(nar.root_node() != nullptr) << "Failed to parse NAR file: " << nar_file;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(AllNarFiles, NarParseTest, ::testing::ValuesIn(NarFiles));
|
||||
@@ -235,7 +235,7 @@ nix_get_string(nix_c_context * context, const nix_value * value, nix_get_string_
|
||||
try {
|
||||
auto & v = check_value_in(value);
|
||||
assert(v.type() == nix::nString);
|
||||
call_nix_get_string_callback(v.c_str(), callback, user_data);
|
||||
call_nix_get_string_callback(v.string_view(), callback, user_data);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s))
|
||||
if (arg.type() != nString) {
|
||||
return false;
|
||||
}
|
||||
return std::string_view(arg.c_str()) == s;
|
||||
return arg.string_view() == s;
|
||||
}
|
||||
|
||||
MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v))
|
||||
|
||||
@@ -139,63 +139,6 @@ TEST_F(ErrorTraceTest, NestedThrows)
|
||||
#define ASSERT_DERIVATION_TRACE3(args, type, message, context1, context2) \
|
||||
ASSERT_TRACE4(args, type, message, context1, context2, DERIVATION_TRACE_HINTFMT("foo"))
|
||||
|
||||
TEST_F(ErrorTraceTest, genericClosure)
|
||||
{
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure 1",
|
||||
TypeError,
|
||||
HintFmt("expected a set but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)),
|
||||
HintFmt("while evaluating the first argument passed to builtins.genericClosure"));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure {}",
|
||||
TypeError,
|
||||
HintFmt("attribute '%s' missing", "startSet"),
|
||||
HintFmt("in the attrset passed as argument to builtins.genericClosure"));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure { startSet = 1; }",
|
||||
TypeError,
|
||||
HintFmt("expected a list but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)),
|
||||
HintFmt("while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure { startSet = [{ key = 1;}]; operator = true; }",
|
||||
TypeError,
|
||||
HintFmt("expected a function but found %s: %s", "a Boolean", Uncolored(ANSI_CYAN "true" ANSI_NORMAL)),
|
||||
HintFmt("while evaluating the 'operator' attribute passed as argument to builtins.genericClosure"));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure { startSet = [{ key = 1;}]; operator = item: true; }",
|
||||
TypeError,
|
||||
HintFmt("expected a list but found %s: %s", "a Boolean", Uncolored(ANSI_CYAN "true" ANSI_NORMAL)),
|
||||
HintFmt("while evaluating the return value of the `operator` passed to builtins.genericClosure"));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure { startSet = [{ key = 1;}]; operator = item: [ true ]; }",
|
||||
TypeError,
|
||||
HintFmt("expected a set but found %s: %s", "a Boolean", Uncolored(ANSI_CYAN "true" ANSI_NORMAL)),
|
||||
HintFmt("while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure { startSet = [{ key = 1;}]; operator = item: [ {} ]; }",
|
||||
TypeError,
|
||||
HintFmt("attribute '%s' missing", "key"),
|
||||
HintFmt("in one of the attrsets generated by (or initially passed to) builtins.genericClosure"));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure { startSet = [{ key = 1;}]; operator = item: [{ key = ''a''; }]; }",
|
||||
EvalError,
|
||||
HintFmt("cannot compare %s with %s", "a string", "an integer"),
|
||||
HintFmt("while comparing the `key` attributes of two genericClosure elements"));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"genericClosure { startSet = [ true ]; operator = item: [{ key = ''a''; }]; }",
|
||||
TypeError,
|
||||
HintFmt("expected a set but found %s: %s", "a Boolean", Uncolored(ANSI_CYAN "true" ANSI_NORMAL)),
|
||||
HintFmt("while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"));
|
||||
}
|
||||
|
||||
TEST_F(ErrorTraceTest, replaceStrings)
|
||||
{
|
||||
ASSERT_TRACE2(
|
||||
@@ -1050,17 +993,35 @@ TEST_F(ErrorTraceTest, bitXor)
|
||||
|
||||
TEST_F(ErrorTraceTest, lessThan)
|
||||
{
|
||||
ASSERT_TRACE1("lessThan 1 \"foo\"", EvalError, HintFmt("cannot compare %s with %s", "an integer", "a string"));
|
||||
ASSERT_TRACE1(
|
||||
"lessThan 1 \"foo\"",
|
||||
EvalError,
|
||||
HintFmt(
|
||||
"cannot compare %s with %s; values are %s and %s",
|
||||
"an integer",
|
||||
"a string",
|
||||
Uncolored(ANSI_CYAN "1" ANSI_NORMAL),
|
||||
Uncolored(ANSI_MAGENTA "\"foo\"" ANSI_NORMAL)));
|
||||
|
||||
ASSERT_TRACE1(
|
||||
"lessThan {} {}",
|
||||
EvalError,
|
||||
HintFmt("cannot compare %s with %s; values of that type are incomparable", "a set", "a set"));
|
||||
HintFmt(
|
||||
"cannot compare %s with %s; values of that type are incomparable (values are %s and %s)",
|
||||
"a set",
|
||||
"a set",
|
||||
Uncolored("{ }"),
|
||||
Uncolored("{ }")));
|
||||
|
||||
ASSERT_TRACE2(
|
||||
"lessThan [ 1 2 ] [ \"foo\" ]",
|
||||
EvalError,
|
||||
HintFmt("cannot compare %s with %s", "an integer", "a string"),
|
||||
HintFmt(
|
||||
"cannot compare %s with %s; values are %s and %s",
|
||||
"an integer",
|
||||
"a string",
|
||||
Uncolored(ANSI_CYAN "1" ANSI_NORMAL),
|
||||
Uncolored(ANSI_MAGENTA "\"foo\"" ANSI_NORMAL)),
|
||||
HintFmt("while comparing two list elements"));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "nix/expr/value.hh"
|
||||
|
||||
#include "nix/store/tests/libstore.hh"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -22,4 +23,21 @@ TEST_F(ValueTest, vInt)
|
||||
ASSERT_EQ(true, vInt.isValid());
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, staticString)
|
||||
{
|
||||
Value vStr1;
|
||||
Value vStr2;
|
||||
vStr1.mkStringNoCopy("foo");
|
||||
vStr2.mkStringNoCopy("foo");
|
||||
|
||||
auto sd1 = vStr1.string_view();
|
||||
auto sd2 = vStr2.string_view();
|
||||
|
||||
// The strings should be the same
|
||||
ASSERT_EQ(sd1, sd2);
|
||||
|
||||
// The strings should also be backed by the same (static) allocation
|
||||
ASSERT_EQ(sd1.data(), sd2.data());
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -136,17 +136,19 @@ struct AttrDb
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setString(AttrKey key, std::string_view s, const char ** context = nullptr)
|
||||
AttrId setString(AttrKey key, std::string_view s, const Value::StringWithContext::Context * context = nullptr)
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
if (context) {
|
||||
std::string ctx;
|
||||
for (const char ** p = context; *p; ++p) {
|
||||
if (p != context)
|
||||
bool first = true;
|
||||
for (auto * elem : *context) {
|
||||
if (!first)
|
||||
ctx.push_back(' ');
|
||||
ctx.append(*p);
|
||||
ctx.append(elem);
|
||||
first = false;
|
||||
}
|
||||
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
|
||||
.exec();
|
||||
@@ -406,7 +408,7 @@ Value & AttrCursor::forceValue()
|
||||
|
||||
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
|
||||
if (v.type() == nString)
|
||||
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()), string_t{v.c_str(), {}}};
|
||||
cachedValue = {root->db->setString(getKey(), v.string_view(), v.context()), string_t{v.string_view(), {}}};
|
||||
else if (v.type() == nPath) {
|
||||
auto path = v.path().path;
|
||||
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
|
||||
@@ -541,7 +543,7 @@ std::string AttrCursor::getString()
|
||||
if (v.type() != nString && v.type() != nPath)
|
||||
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr(), showType(v)).debugThrow();
|
||||
|
||||
return v.type() == nString ? v.c_str() : v.path().to_string();
|
||||
return v.type() == nString ? std::string(v.string_view()) : v.path().to_string();
|
||||
}
|
||||
|
||||
string_t AttrCursor::getStringWithContext()
|
||||
@@ -580,7 +582,7 @@ string_t AttrCursor::getStringWithContext()
|
||||
if (v.type() == nString) {
|
||||
NixStringContext context;
|
||||
copyContext(v, context);
|
||||
return {v.c_str(), std::move(context)};
|
||||
return {std::string{v.string_view()}, std::move(context)};
|
||||
} else if (v.type() == nPath)
|
||||
return {v.path().to_string(), {}};
|
||||
else
|
||||
|
||||
@@ -92,7 +92,7 @@ bool EvalSettings::isPseudoUrl(std::string_view s)
|
||||
std::string EvalSettings::resolvePseudoUrl(std::string_view url)
|
||||
{
|
||||
if (hasPrefix(url, "channel:"))
|
||||
return "https://nixos.org/channels/" + std::string(url.substr(8)) + "/nixexprs.tar.xz";
|
||||
return "https://channels.nixos.org/" + std::string(url.substr(8)) + "/nixexprs.tar.xz";
|
||||
else
|
||||
return std::string(url);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <sys/time.h>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <ranges>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
@@ -821,28 +822,25 @@ void Value::mkString(std::string_view s)
|
||||
mkStringNoCopy(makeImmutableString(s));
|
||||
}
|
||||
|
||||
static const char ** encodeContext(const NixStringContext & context)
|
||||
Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
|
||||
{
|
||||
if (!context.empty()) {
|
||||
size_t n = 0;
|
||||
auto ctx = (const char **) allocBytes((context.size() + 1) * sizeof(char *));
|
||||
for (auto & i : context) {
|
||||
ctx[n++] = makeImmutableString({i.to_string()});
|
||||
}
|
||||
ctx[n] = nullptr;
|
||||
return ctx;
|
||||
} else
|
||||
if (context.empty())
|
||||
return nullptr;
|
||||
|
||||
auto ctx = new (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()); });
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void Value::mkString(std::string_view s, const NixStringContext & context)
|
||||
{
|
||||
mkStringNoCopy(makeImmutableString(s), encodeContext(context));
|
||||
mkStringNoCopy(makeImmutableString(s), Value::StringWithContext::Context::fromBuilder(context));
|
||||
}
|
||||
|
||||
void Value::mkStringMove(const char * s, const NixStringContext & context)
|
||||
{
|
||||
mkStringNoCopy(s, encodeContext(context));
|
||||
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context));
|
||||
}
|
||||
|
||||
void Value::mkPath(const SourcePath & path)
|
||||
@@ -1113,6 +1111,7 @@ void EvalState::resetFileCache()
|
||||
importResolutionCache->clear();
|
||||
fileEvalCache->clear();
|
||||
inputCache->clear();
|
||||
positions.clear();
|
||||
}
|
||||
|
||||
void EvalState::eval(Expr * e, Value & v)
|
||||
@@ -2287,9 +2286,9 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
|
||||
|
||||
void copyContext(const Value & v, NixStringContext & context, const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
if (v.context())
|
||||
for (const char ** p = v.context(); *p; ++p)
|
||||
context.insert(NixStringContextElem::parse(*p, xpSettings));
|
||||
if (auto * ctx = v.context())
|
||||
for (auto * elem : *ctx)
|
||||
context.insert(NixStringContextElem::parse(elem, xpSettings));
|
||||
}
|
||||
|
||||
std::string_view EvalState::forceString(
|
||||
@@ -2309,7 +2308,9 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
|
||||
auto s = forceString(v, pos, errorCtx);
|
||||
if (v.context()) {
|
||||
error<EvalError>(
|
||||
"the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string_view(), v.context()[0])
|
||||
"the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||
v.string_view(),
|
||||
*v.context()->begin())
|
||||
.withTrace(pos, errorCtx)
|
||||
.debugThrow();
|
||||
}
|
||||
@@ -2366,12 +2367,15 @@ BackedStringView EvalState::coerceToString(
|
||||
}
|
||||
|
||||
if (v.type() == nPath) {
|
||||
return !canonicalizePath && !copyToStore
|
||||
? // FIXME: hack to preserve path literals that end in a
|
||||
// slash, as in /foo/${x}.
|
||||
v.pathStr()
|
||||
: copyToStore ? store->printStorePath(copyPathToStore(context, v.path()))
|
||||
: std::string(v.path().path.abs());
|
||||
if (!canonicalizePath && !copyToStore) {
|
||||
// FIXME: hack to preserve path literals that end in a
|
||||
// slash, as in /foo/${x}.
|
||||
return v.pathStrView();
|
||||
} else if (copyToStore) {
|
||||
return store->printStorePath(copyPathToStore(context, v.path()));
|
||||
} else {
|
||||
return std::string{v.path().path.abs()};
|
||||
}
|
||||
}
|
||||
|
||||
if (v.type() == nAttrs) {
|
||||
@@ -2624,7 +2628,7 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
|
||||
return;
|
||||
|
||||
case nString:
|
||||
if (strcmp(v1.c_str(), v2.c_str()) != 0) {
|
||||
if (v1.string_view() != v2.string_view()) {
|
||||
error<AssertionError>(
|
||||
"string '%s' is not equal to string '%s'",
|
||||
ValuePrinter(*this, v1, errorPrintOptions),
|
||||
@@ -2641,7 +2645,7 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
|
||||
ValuePrinter(*this, v2, errorPrintOptions))
|
||||
.debugThrow();
|
||||
}
|
||||
if (strcmp(v1.pathStr(), v2.pathStr()) != 0) {
|
||||
if (v1.pathStrView() != v2.pathStrView()) {
|
||||
error<AssertionError>(
|
||||
"path '%s' is not equal to path '%s'",
|
||||
ValuePrinter(*this, v1, errorPrintOptions),
|
||||
@@ -2807,12 +2811,12 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
||||
return v1.boolean() == v2.boolean();
|
||||
|
||||
case nString:
|
||||
return strcmp(v1.c_str(), v2.c_str()) == 0;
|
||||
return v1.string_view() == v2.string_view();
|
||||
|
||||
case nPath:
|
||||
return
|
||||
// FIXME: compare accessors by their fingerprint.
|
||||
v1.pathAccessor() == v2.pathAccessor() && strcmp(v1.pathStr(), v2.pathStr()) == 0;
|
||||
v1.pathAccessor() == v2.pathAccessor() && v1.pathStrView() == v2.pathStrView();
|
||||
|
||||
case nNull:
|
||||
return true;
|
||||
@@ -3213,8 +3217,8 @@ Expr * EvalState::parse(
|
||||
docComments = &it->second;
|
||||
}
|
||||
|
||||
auto result = parseExprFromBuf(
|
||||
text, length, origin, basePath, mem.exprs.alloc, symbols, settings, positions, *docComments, rootFS);
|
||||
auto result =
|
||||
parseExprFromBuf(text, length, origin, basePath, mem.exprs, symbols, settings, positions, *docComments, rootFS);
|
||||
|
||||
result->bindVars(*this, staticEnv);
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
||||
for (auto elem : outTI->listView()) {
|
||||
if (elem->type() != nString)
|
||||
throw errMsg;
|
||||
auto out = outputs.find(elem->c_str());
|
||||
auto out = outputs.find(elem->string_view());
|
||||
if (out == outputs.end())
|
||||
throw errMsg;
|
||||
result.insert(*out);
|
||||
@@ -245,7 +245,7 @@ std::string PackageInfo::queryMetaString(const std::string & name)
|
||||
Value * v = queryMeta(name);
|
||||
if (!v || v->type() != nString)
|
||||
return "";
|
||||
return v->c_str();
|
||||
return std::string{v->string_view()};
|
||||
}
|
||||
|
||||
NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
|
||||
@@ -258,7 +258,7 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
integer meta fields. */
|
||||
if (auto n = string2Int<NixInt::Inner>(v->c_str()))
|
||||
if (auto n = string2Int<NixInt::Inner>(v->string_view()))
|
||||
return NixInt{*n};
|
||||
}
|
||||
return def;
|
||||
@@ -274,7 +274,7 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
float meta fields. */
|
||||
if (auto n = string2Float<NixFloat>(v->c_str()))
|
||||
if (auto n = string2Float<NixFloat>(v->string_view()))
|
||||
return *n;
|
||||
}
|
||||
return def;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace nix {
|
||||
struct PackageInfo
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, std::optional<StorePath>> Outputs;
|
||||
typedef std::map<std::string, std::optional<StorePath>, std::less<>> Outputs;
|
||||
|
||||
private:
|
||||
EvalState * state;
|
||||
|
||||
@@ -91,13 +91,6 @@ std::string showAttrPath(const SymbolTable & symbols, std::span<const AttrName>
|
||||
|
||||
using UpdateQueue = SmallTemporaryValueVector<conservativeStackReservation>;
|
||||
|
||||
class Exprs
|
||||
{
|
||||
std::pmr::monotonic_buffer_resource buffer;
|
||||
public:
|
||||
std::pmr::polymorphic_allocator<char> alloc{&buffer};
|
||||
};
|
||||
|
||||
/* Abstract syntax of Nix expressions. */
|
||||
|
||||
struct Expr
|
||||
@@ -637,8 +630,8 @@ struct ExprLet : Expr
|
||||
struct ExprWith : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
uint32_t prevWith;
|
||||
Expr *attrs, *body;
|
||||
size_t prevWith;
|
||||
ExprWith * parentWith;
|
||||
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body)
|
||||
: pos(pos)
|
||||
@@ -760,11 +753,19 @@ struct ExprConcatStrings : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
bool forceString;
|
||||
std::vector<std::pair<PosIdx, Expr *>> es;
|
||||
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> && es)
|
||||
std::span<std::pair<PosIdx, Expr *>> es;
|
||||
|
||||
ExprConcatStrings(
|
||||
std::pmr::polymorphic_allocator<char> & alloc,
|
||||
const PosIdx & pos,
|
||||
bool forceString,
|
||||
const std::vector<std::pair<PosIdx, Expr *>> & es)
|
||||
: pos(pos)
|
||||
, forceString(forceString)
|
||||
, es(std::move(es)) {};
|
||||
, es({alloc.allocate_object<std::pair<PosIdx, Expr *>>(es.size()), es.size()})
|
||||
{
|
||||
std::ranges::copy(es, this->es.begin());
|
||||
};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
@@ -802,6 +803,49 @@ struct ExprBlackHole : Expr
|
||||
|
||||
extern ExprBlackHole eBlackHole;
|
||||
|
||||
class Exprs
|
||||
{
|
||||
std::pmr::monotonic_buffer_resource buffer;
|
||||
public:
|
||||
std::pmr::polymorphic_allocator<char> alloc{&buffer};
|
||||
|
||||
template<class C>
|
||||
[[gnu::always_inline]]
|
||||
C * add(auto &&... args)
|
||||
{
|
||||
return alloc.new_object<C>(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
|
||||
// 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)
|
||||
requires(std::same_as<C, ExprCall>)
|
||||
{
|
||||
return alloc.new_object<C>(pos, fun, std::move(args));
|
||||
}
|
||||
|
||||
template<class C>
|
||||
[[gnu::always_inline]]
|
||||
C * add(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos)
|
||||
requires(std::same_as<C, ExprCall>)
|
||||
{
|
||||
return alloc.new_object<C>(pos, fun, std::move(args), std::move(cursedOrEndPos));
|
||||
}
|
||||
|
||||
template<class C>
|
||||
[[gnu::always_inline]]
|
||||
C *
|
||||
add(std::pmr::polymorphic_allocator<char> & alloc,
|
||||
const PosIdx & pos,
|
||||
bool forceString,
|
||||
const std::vector<std::pair<PosIdx, Expr *>> & es)
|
||||
requires(std::same_as<C, ExprConcatStrings>)
|
||||
{
|
||||
return alloc.new_object<C>(alloc, pos, forceString, es);
|
||||
}
|
||||
};
|
||||
|
||||
/* Static environments are used to map variable names onto (level,
|
||||
displacement) pairs used to obtain the value of the variable at
|
||||
runtime. */
|
||||
|
||||
@@ -78,7 +78,7 @@ struct LexerState
|
||||
struct ParserState
|
||||
{
|
||||
const LexerState & lexerState;
|
||||
std::pmr::polymorphic_allocator<char> & alloc;
|
||||
Exprs & exprs;
|
||||
SymbolTable & symbols;
|
||||
PosTable & positions;
|
||||
Expr * result;
|
||||
@@ -132,11 +132,11 @@ inline void ParserState::addAttr(
|
||||
dupAttr(attrPath, pos, j->second.pos);
|
||||
}
|
||||
} else {
|
||||
nested = new ExprAttrs;
|
||||
nested = exprs.add<ExprAttrs>();
|
||||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
||||
}
|
||||
} else {
|
||||
nested = new ExprAttrs;
|
||||
nested = exprs.add<ExprAttrs>();
|
||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
|
||||
}
|
||||
attrs = nested;
|
||||
@@ -240,7 +240,7 @@ inline Expr *
|
||||
ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||
{
|
||||
if (es.empty())
|
||||
return new ExprString("");
|
||||
return exprs.add<ExprString>("");
|
||||
|
||||
/* Figure out the minimum indentation. Note that by design
|
||||
whitespace-only final lines are not taken into account. (So
|
||||
@@ -322,7 +322,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, st
|
||||
|
||||
// Ignore empty strings for a minor optimisation and AST simplification
|
||||
if (s2 != "") {
|
||||
es2.emplace_back(i->first, new ExprString(alloc, s2));
|
||||
es2.emplace_back(i->first, exprs.add<ExprString>(exprs.alloc, s2));
|
||||
}
|
||||
};
|
||||
for (; i != es.end(); ++i, --n) {
|
||||
@@ -332,7 +332,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 = new ExprString("");
|
||||
auto * const result = exprs.add<ExprString>("");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, st
|
||||
auto * const result = (es2)[0].second;
|
||||
return result;
|
||||
}
|
||||
return new ExprConcatStrings(pos, true, std::move(es2));
|
||||
return exprs.add<ExprConcatStrings>(exprs.alloc, pos, true, std::move(es2));
|
||||
}
|
||||
|
||||
inline PosIdx LexerState::at(const ParserLocation & loc)
|
||||
|
||||
@@ -220,7 +220,66 @@ struct ValueBase
|
||||
struct StringWithContext
|
||||
{
|
||||
const char * c_str;
|
||||
const char ** context; // must be in sorted order
|
||||
|
||||
/**
|
||||
* The type of the context itself.
|
||||
*
|
||||
* Currently, it is length-prefixed array of pointers to
|
||||
* null-terminated strings. The strings are specially formatted
|
||||
* to represent a flattening of the recursive sum type that is a
|
||||
* context element.
|
||||
*
|
||||
* @See NixStringContext for an more easily understood type,
|
||||
* that of the "builder" for this data structure.
|
||||
*/
|
||||
struct Context
|
||||
{
|
||||
using value_type = const char *;
|
||||
using size_type = std::size_t;
|
||||
using iterator = const value_type *;
|
||||
|
||||
Context(size_type size)
|
||||
: size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Number of items in the array
|
||||
*/
|
||||
size_type size_;
|
||||
|
||||
/**
|
||||
* @pre must be in sorted order
|
||||
*/
|
||||
value_type elems[];
|
||||
|
||||
public:
|
||||
iterator begin() const
|
||||
{
|
||||
return elems;
|
||||
}
|
||||
|
||||
iterator end() const
|
||||
{
|
||||
return elems + size();
|
||||
}
|
||||
|
||||
size_type size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null pointer when context.empty()
|
||||
*/
|
||||
static Context * fromBuilder(const NixStringContext & context);
|
||||
};
|
||||
|
||||
/**
|
||||
* May be null for a string without context.
|
||||
*/
|
||||
const Context * context;
|
||||
};
|
||||
|
||||
struct Path
|
||||
@@ -991,7 +1050,7 @@ public:
|
||||
setStorage(b);
|
||||
}
|
||||
|
||||
void mkStringNoCopy(const char * s, const char ** context = 0) noexcept
|
||||
void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept
|
||||
{
|
||||
setStorage(StringWithContext{.c_str = s, .context = context});
|
||||
}
|
||||
@@ -1109,7 +1168,7 @@ public:
|
||||
|
||||
std::string_view string_view() const noexcept
|
||||
{
|
||||
return std::string_view(getStorage<StringWithContext>().c_str);
|
||||
return std::string_view{getStorage<StringWithContext>().c_str};
|
||||
}
|
||||
|
||||
const char * c_str() const noexcept
|
||||
@@ -1117,7 +1176,7 @@ public:
|
||||
return getStorage<StringWithContext>().c_str;
|
||||
}
|
||||
|
||||
const char ** context() const noexcept
|
||||
const Value::StringWithContext::Context * context() const noexcept
|
||||
{
|
||||
return getStorage<StringWithContext>().context;
|
||||
}
|
||||
@@ -1177,6 +1236,11 @@ public:
|
||||
return getStorage<Path>().path;
|
||||
}
|
||||
|
||||
std::string_view pathStrView() const noexcept
|
||||
{
|
||||
return std::string_view{getStorage<Path>().path};
|
||||
}
|
||||
|
||||
SourceAccessor * pathAccessor() const noexcept
|
||||
{
|
||||
return getStorage<Path>().accessor;
|
||||
|
||||
@@ -24,6 +24,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @todo This should be renamed to `StringContextBuilderElem`, since:
|
||||
*
|
||||
* 1. We use `*Builder` for off-heap temporary data structures
|
||||
*
|
||||
* 2. The `Nix*` is totally redundant. (And my mistake from a long time
|
||||
* ago.)
|
||||
*/
|
||||
struct NixStringContextElem
|
||||
{
|
||||
/**
|
||||
@@ -77,6 +85,11 @@ struct NixStringContextElem
|
||||
std::string to_string() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @todo This should be renamed to `StringContextBuilder`.
|
||||
*
|
||||
* @see NixStringContextElem for explanation why.
|
||||
*/
|
||||
typedef std::set<NixStringContextElem> NixStringContext;
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -45,7 +45,7 @@ void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
|
||||
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
str << v.pathStr();
|
||||
str << v.pathStrView();
|
||||
}
|
||||
|
||||
void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
@@ -523,6 +523,7 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
prevWith = 0;
|
||||
for (curEnv = env.get(), level = 1; curEnv; curEnv = curEnv->up.get(), level++)
|
||||
if (curEnv->isWith) {
|
||||
assert(level <= std::numeric_limits<uint32_t>::max());
|
||||
prevWith = level;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ Expr * parseExprFromBuf(
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
std::pmr::polymorphic_allocator<char> & alloc,
|
||||
Exprs & exprs,
|
||||
SymbolTable & symbols,
|
||||
const EvalSettings & settings,
|
||||
PosTable & positions,
|
||||
@@ -113,12 +113,12 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P
|
||||
}
|
||||
}
|
||||
|
||||
static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) {
|
||||
static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
|
||||
if (auto e2 = dynamic_cast<ExprCall *>(fn)) {
|
||||
e2->args.push_back(arg);
|
||||
return fn;
|
||||
}
|
||||
return new ExprCall(pos, fn, {arg});
|
||||
return exprs.add<ExprCall>(pos, fn, {arg});
|
||||
}
|
||||
|
||||
|
||||
@@ -179,14 +179,14 @@ expr: expr_function;
|
||||
|
||||
expr_function
|
||||
: ID ':' expr_function
|
||||
{ auto me = new ExprLambda(CUR_POS, state->symbols.create($1), $3);
|
||||
{ auto me = state->exprs.add<ExprLambda>(CUR_POS, state->symbols.create($1), $3);
|
||||
$$ = me;
|
||||
SET_DOC_POS(me, @1);
|
||||
}
|
||||
| formal_set ':' expr_function[body]
|
||||
{
|
||||
state->validateFormals($formal_set);
|
||||
auto me = new ExprLambda(state->positions, state->alloc, CUR_POS, std::move($formal_set), $body);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, std::move($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 = new ExprLambda(state->positions, state->alloc, CUR_POS, arg, std::move($formal_set), $body);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, std::move($formal_set), $body);
|
||||
$$ = me;
|
||||
SET_DOC_POS(me, @1);
|
||||
}
|
||||
@@ -202,67 +202,67 @@ expr_function
|
||||
{
|
||||
auto arg = state->symbols.create($ID);
|
||||
state->validateFormals($formal_set, CUR_POS, arg);
|
||||
auto me = new ExprLambda(state->positions, state->alloc, CUR_POS, arg, std::move($formal_set), $body);
|
||||
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, std::move($formal_set), $body);
|
||||
$$ = me;
|
||||
SET_DOC_POS(me, @1);
|
||||
}
|
||||
| ASSERT expr ';' expr_function
|
||||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||
{ $$ = state->exprs.add<ExprAssert>(CUR_POS, $2, $4); }
|
||||
| WITH expr ';' expr_function
|
||||
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
||||
{ $$ = state->exprs.add<ExprWith>(CUR_POS, $2, $4); }
|
||||
| LET binds IN_KW expr_function
|
||||
{ if (!$2->dynamicAttrs.empty())
|
||||
throw ParseError({
|
||||
.msg = HintFmt("dynamic attributes not allowed in let"),
|
||||
.pos = state->positions[CUR_POS]
|
||||
});
|
||||
$$ = new ExprLet($2, $4);
|
||||
$$ = state->exprs.add<ExprLet>($2, $4);
|
||||
}
|
||||
| expr_if
|
||||
;
|
||||
|
||||
expr_if
|
||||
: IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, $2, $4, $6); }
|
||||
: IF expr THEN expr ELSE expr { $$ = state->exprs.add<ExprIf>(CUR_POS, $2, $4, $6); }
|
||||
| expr_pipe_from
|
||||
| expr_pipe_into
|
||||
| expr_op
|
||||
;
|
||||
|
||||
expr_pipe_from
|
||||
: expr_op PIPE_FROM expr_pipe_from { $$ = makeCall(state->at(@2), $1, $3); }
|
||||
| expr_op PIPE_FROM expr_op { $$ = makeCall(state->at(@2), $1, $3); }
|
||||
: expr_op PIPE_FROM expr_pipe_from { $$ = makeCall(state->exprs, state->at(@2), $1, $3); }
|
||||
| expr_op PIPE_FROM expr_op { $$ = makeCall(state->exprs, state->at(@2), $1, $3); }
|
||||
;
|
||||
|
||||
expr_pipe_into
|
||||
: expr_pipe_into PIPE_INTO expr_op { $$ = makeCall(state->at(@2), $3, $1); }
|
||||
| expr_op PIPE_INTO expr_op { $$ = makeCall(state->at(@2), $3, $1); }
|
||||
: expr_pipe_into PIPE_INTO expr_op { $$ = makeCall(state->exprs, state->at(@2), $3, $1); }
|
||||
| expr_op PIPE_INTO expr_op { $$ = makeCall(state->exprs, state->at(@2), $3, $1); }
|
||||
;
|
||||
|
||||
expr_op
|
||||
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
|
||||
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(state->s.sub), {new ExprInt(0), $2}); }
|
||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
||||
| expr_op '<' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.lessThan), {$1, $3}); }
|
||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(state->at(@2), new ExprVar(state->s.lessThan), {$3, $1})); }
|
||||
| expr_op '>' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.lessThan), {$3, $1}); }
|
||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(state->at(@2), new ExprVar(state->s.lessThan), {$1, $3})); }
|
||||
| expr_op AND expr_op { $$ = new ExprOpAnd(state->at(@2), $1, $3); }
|
||||
| expr_op OR expr_op { $$ = new ExprOpOr(state->at(@2), $1, $3); }
|
||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl(state->at(@2), $1, $3); }
|
||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(state->at(@2), $1, $3); }
|
||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr(state->alloc, $1, std::move($3)); }
|
||||
: '!' expr_op %prec NOT { $$ = state->exprs.add<ExprOpNot>($2); }
|
||||
| '-' expr_op %prec NEGATE { $$ = state->exprs.add<ExprCall>(CUR_POS, state->exprs.add<ExprVar>(state->s.sub), {state->exprs.add<ExprInt>(0), $2}); }
|
||||
| expr_op EQ expr_op { $$ = state->exprs.add<ExprOpEq>($1, $3); }
|
||||
| expr_op NEQ expr_op { $$ = state->exprs.add<ExprOpNEq>($1, $3); }
|
||||
| expr_op '<' expr_op { $$ = state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.lessThan), {$1, $3}); }
|
||||
| expr_op LEQ expr_op { $$ = state->exprs.add<ExprOpNot>(state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.lessThan), {$3, $1})); }
|
||||
| expr_op '>' expr_op { $$ = state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.lessThan), {$3, $1}); }
|
||||
| expr_op GEQ expr_op { $$ = state->exprs.add<ExprOpNot>(state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.lessThan), {$1, $3})); }
|
||||
| expr_op AND expr_op { $$ = state->exprs.add<ExprOpAnd>(state->at(@2), $1, $3); }
|
||||
| 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 '+' expr_op
|
||||
{ $$ = new ExprConcatStrings(state->at(@2), false, {{state->at(@1), $1}, {state->at(@3), $3}}); }
|
||||
| expr_op '-' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.sub), {$1, $3}); }
|
||||
| expr_op '*' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.mul), {$1, $3}); }
|
||||
| expr_op '/' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.div), {$1, $3}); }
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(state->at(@2), $1, $3); }
|
||||
{ $$ = 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}); }
|
||||
| expr_op '*' expr_op { $$ = state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.mul), {$1, $3}); }
|
||||
| expr_op '/' expr_op { $$ = state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.div), {$1, $3}); }
|
||||
| expr_op CONCAT expr_op { $$ = state->exprs.add<ExprOpConcatLists>(state->at(@2), $1, $3); }
|
||||
| expr_app
|
||||
;
|
||||
|
||||
expr_app
|
||||
: expr_app expr_select { $$ = makeCall(CUR_POS, $1, $2); $2->warnIfCursedOr(state->symbols, state->positions); }
|
||||
: expr_app expr_select { $$ = makeCall(state->exprs, CUR_POS, $1, $2); $2->warnIfCursedOr(state->symbols, state->positions); }
|
||||
| /* Once a ‘cursed or’ reaches this nonterminal, it is no longer cursed,
|
||||
because the uncursed parse would also produce an expr_app. But we need
|
||||
to remove the cursed status in order to prevent valid things like
|
||||
@@ -272,9 +272,9 @@ expr_app
|
||||
|
||||
expr_select
|
||||
: expr_simple '.' attrpath
|
||||
{ $$ = new ExprSelect(state->alloc, CUR_POS, $1, std::move($3), nullptr); }
|
||||
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, std::move($3), nullptr); }
|
||||
| expr_simple '.' attrpath OR_KW expr_select
|
||||
{ $$ = new ExprSelect(state->alloc, CUR_POS, $1, std::move($3), $5); $5->warnIfCursedOr(state->symbols, state->positions); }
|
||||
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, std::move($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
|
||||
@@ -283,7 +283,7 @@ expr_select
|
||||
the ExprCall with data (establishing that it is a ‘cursed or’) that can
|
||||
be used to emit a warning when an affected expression is parsed. */
|
||||
expr_simple OR_KW
|
||||
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, state->s.or_)}, state->positions.add(state->origin, @$.endOffset)); }
|
||||
{ $$ = state->exprs.add<ExprCall>(CUR_POS, $1, {state->exprs.add<ExprVar>(CUR_POS, state->s.or_)}, state->positions.add(state->origin, @$.endOffset)); }
|
||||
| expr_simple
|
||||
;
|
||||
|
||||
@@ -291,15 +291,15 @@ expr_simple
|
||||
: ID {
|
||||
std::string_view s = "__curPos";
|
||||
if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0)
|
||||
$$ = new ExprPos(CUR_POS);
|
||||
$$ = state->exprs.add<ExprPos>(CUR_POS);
|
||||
else
|
||||
$$ = new ExprVar(CUR_POS, state->symbols.create($1));
|
||||
$$ = state->exprs.add<ExprVar>(CUR_POS, state->symbols.create($1));
|
||||
}
|
||||
| INT_LIT { $$ = new ExprInt($1); }
|
||||
| FLOAT_LIT { $$ = new ExprFloat($1); }
|
||||
| INT_LIT { $$ = state->exprs.add<ExprInt>($1); }
|
||||
| FLOAT_LIT { $$ = state->exprs.add<ExprFloat>($1); }
|
||||
| '"' string_parts '"' {
|
||||
std::visit(overloaded{
|
||||
[&](std::string_view str) { $$ = new ExprString(state->alloc, str); },
|
||||
[&](std::string_view str) { $$ = state->exprs.add<ExprString>(state->exprs.alloc, str); },
|
||||
[&](Expr * expr) { $$ = expr; }},
|
||||
$2);
|
||||
}
|
||||
@@ -309,14 +309,14 @@ expr_simple
|
||||
| path_start PATH_END
|
||||
| path_start string_parts_interpolated PATH_END {
|
||||
$2.insert($2.begin(), {state->at(@1), $1});
|
||||
$$ = new ExprConcatStrings(CUR_POS, false, std::move($2));
|
||||
$$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, false, std::move($2));
|
||||
}
|
||||
| SPATH {
|
||||
std::string_view path($1.p + 1, $1.l - 2);
|
||||
$$ = new ExprCall(CUR_POS,
|
||||
new ExprVar(state->s.findFile),
|
||||
{new ExprVar(state->s.nixPath),
|
||||
new ExprString(state->alloc, path)});
|
||||
$$ = state->exprs.add<ExprCall>(CUR_POS,
|
||||
state->exprs.add<ExprVar>(state->s.findFile),
|
||||
{state->exprs.add<ExprVar>(state->s.nixPath),
|
||||
state->exprs.add<ExprString>(state->exprs.alloc, path)});
|
||||
}
|
||||
| URI {
|
||||
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
|
||||
@@ -325,35 +325,35 @@ expr_simple
|
||||
.msg = HintFmt("URL literals are disabled"),
|
||||
.pos = state->positions[CUR_POS]
|
||||
});
|
||||
$$ = new ExprString(state->alloc, $1);
|
||||
$$ = state->exprs.add<ExprString>(state->exprs.alloc, $1);
|
||||
}
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
/* Let expressions `let {..., body = ...}' are just desugared
|
||||
into `(rec {..., body = ...}).body'. */
|
||||
| LET '{' binds '}'
|
||||
{ $3->recursive = true; $3->pos = CUR_POS; $$ = new ExprSelect(state->alloc, noPos, $3, state->s.body); }
|
||||
{ $3->recursive = true; $3->pos = CUR_POS; $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, noPos, $3, state->s.body); }
|
||||
| REC '{' binds '}'
|
||||
{ $3->recursive = true; $3->pos = CUR_POS; $$ = $3; }
|
||||
| '{' binds1 '}'
|
||||
{ $2->pos = CUR_POS; $$ = $2; }
|
||||
| '{' '}'
|
||||
{ $$ = new ExprAttrs(CUR_POS); }
|
||||
| '[' list ']' { $$ = new ExprList(state->alloc, std::move($2)); }
|
||||
{ $$ = state->exprs.add<ExprAttrs>(CUR_POS); }
|
||||
| '[' list ']' { $$ = state->exprs.add<ExprList>(state->exprs.alloc, std::move($2)); }
|
||||
;
|
||||
|
||||
string_parts
|
||||
: STR { $$ = $1; }
|
||||
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, std::move($1)); }
|
||||
| string_parts_interpolated { $$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, true, std::move($1)); }
|
||||
| { $$ = std::string_view(); }
|
||||
;
|
||||
|
||||
string_parts_interpolated
|
||||
: string_parts_interpolated STR
|
||||
{ $$ = std::move($1); $$.emplace_back(state->at(@2), new ExprString(state->alloc, $2)); }
|
||||
{ $$ = std::move($1); $$.emplace_back(state->at(@2), state->exprs.add<ExprString>(state->exprs.alloc, $2)); }
|
||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = std::move($1); $$.emplace_back(state->at(@2), $3); }
|
||||
| DOLLAR_CURLY expr '}' { $$.emplace_back(state->at(@1), $2); }
|
||||
| STR DOLLAR_CURLY expr '}' {
|
||||
$$.emplace_back(state->at(@1), new ExprString(state->alloc, $1));
|
||||
$$.emplace_back(state->at(@1), state->exprs.add<ExprString>(state->exprs.alloc, $1));
|
||||
$$.emplace_back(state->at(@2), $3);
|
||||
}
|
||||
;
|
||||
@@ -379,8 +379,8 @@ path_start
|
||||
root filesystem accessor, rather than the accessor of the
|
||||
current Nix expression. */
|
||||
literal.front() == '/'
|
||||
? new ExprPath(state->alloc, state->rootFS, path)
|
||||
: new ExprPath(state->alloc, state->basePath.accessor, path);
|
||||
? state->exprs.add<ExprPath>(state->exprs.alloc, state->rootFS, path)
|
||||
: state->exprs.add<ExprPath>(state->exprs.alloc, state->basePath.accessor, path);
|
||||
}
|
||||
| HPATH {
|
||||
if (state->settings.pureEval) {
|
||||
@@ -390,7 +390,7 @@ path_start
|
||||
);
|
||||
}
|
||||
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
|
||||
$$ = new ExprPath(state->alloc, ref<SourceAccessor>(state->rootFS), path);
|
||||
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, ref<SourceAccessor>(state->rootFS), path);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -402,7 +402,7 @@ ind_string_parts
|
||||
|
||||
binds
|
||||
: binds1
|
||||
| { $$ = new ExprAttrs; }
|
||||
| { $$ = state->exprs.add<ExprAttrs>(); }
|
||||
;
|
||||
|
||||
binds1
|
||||
@@ -417,7 +417,7 @@ binds1
|
||||
state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos);
|
||||
$accum->attrs.emplace(
|
||||
i.symbol,
|
||||
ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
|
||||
ExprAttrs::AttrDef(state->exprs.add<ExprVar>(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
|
||||
}
|
||||
}
|
||||
| binds[accum] INHERIT '(' expr ')' attrs ';'
|
||||
@@ -432,13 +432,13 @@ binds1
|
||||
$accum->attrs.emplace(
|
||||
i.symbol,
|
||||
ExprAttrs::AttrDef(
|
||||
new ExprSelect(state->alloc, iPos, from, i.symbol),
|
||||
state->exprs.add<ExprSelect>(state->exprs.alloc, iPos, from, i.symbol),
|
||||
iPos,
|
||||
ExprAttrs::AttrDef::Kind::InheritedFrom));
|
||||
}
|
||||
}
|
||||
| attrpath '=' expr ';'
|
||||
{ $$ = new ExprAttrs;
|
||||
{ $$ = state->exprs.add<ExprAttrs>();
|
||||
state->addAttr($$, std::move($attrpath), @attrpath, $expr, @expr);
|
||||
}
|
||||
;
|
||||
@@ -525,7 +525,7 @@ Expr * parseExprFromBuf(
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
std::pmr::polymorphic_allocator<char> & alloc,
|
||||
Exprs & exprs,
|
||||
SymbolTable & symbols,
|
||||
const EvalSettings & settings,
|
||||
PosTable & positions,
|
||||
@@ -540,7 +540,7 @@ Expr * parseExprFromBuf(
|
||||
};
|
||||
ParserState state {
|
||||
.lexerState = lexerState,
|
||||
.alloc = alloc,
|
||||
.exprs = exprs,
|
||||
.symbols = symbols,
|
||||
.positions = positions,
|
||||
.basePath = basePath,
|
||||
|
||||
@@ -681,7 +681,14 @@ struct CompareValues
|
||||
if (v1->type() == nInt && v2->type() == nFloat)
|
||||
return v1->integer().value < v2->fpoint();
|
||||
if (v1->type() != v2->type())
|
||||
state.error<EvalError>("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
"cannot compare %s with %s; values are %s and %s",
|
||||
showType(*v1),
|
||||
showType(*v2),
|
||||
ValuePrinter(state, *v1, errorPrintOptions),
|
||||
ValuePrinter(state, *v2, errorPrintOptions))
|
||||
.debugThrow();
|
||||
// Allow selecting a subset of enum values
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
@@ -691,12 +698,12 @@ struct CompareValues
|
||||
case nFloat:
|
||||
return v1->fpoint() < v2->fpoint();
|
||||
case nString:
|
||||
return strcmp(v1->c_str(), v2->c_str()) < 0;
|
||||
return v1->string_view() < v2->string_view();
|
||||
case nPath:
|
||||
// Note: we don't take the accessor into account
|
||||
// since it's not obvious how to compare them in a
|
||||
// reproducible way.
|
||||
return strcmp(v1->pathStr(), v2->pathStr()) < 0;
|
||||
return v1->pathStrView() < v2->pathStrView();
|
||||
case nList:
|
||||
// Lexicographic comparison
|
||||
for (size_t i = 0;; i++) {
|
||||
@@ -711,7 +718,11 @@ struct CompareValues
|
||||
default:
|
||||
state
|
||||
.error<EvalError>(
|
||||
"cannot compare %s with %s; values of that type are incomparable", showType(*v1), showType(*v2))
|
||||
"cannot compare %s with %s; values of that type are incomparable (values are %s and %s)",
|
||||
showType(*v1),
|
||||
showType(*v2),
|
||||
ValuePrinter(state, *v1, errorPrintOptions),
|
||||
ValuePrinter(state, *v2, errorPrintOptions))
|
||||
.debugThrow();
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
@@ -757,42 +768,79 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value ** ar
|
||||
`workSet', adding the result to `workSet', continuing until
|
||||
no new elements are found. */
|
||||
ValueList res;
|
||||
// `doneKeys' doesn't need to be a GC root, because its values are
|
||||
// reachable from res.
|
||||
auto cmp = CompareValues(state, noPos, "while comparing the `key` attributes of two genericClosure elements");
|
||||
std::set<Value *, decltype(cmp)> doneKeys(cmp);
|
||||
// Track which element each key came from
|
||||
auto cmp = CompareValues(state, noPos, "");
|
||||
std::map<Value *, Value *, decltype(cmp)> keyToElem(cmp);
|
||||
while (!workSet.empty()) {
|
||||
Value * e = *(workSet.begin());
|
||||
workSet.pop_front();
|
||||
|
||||
state.forceAttrs(
|
||||
*e,
|
||||
noPos,
|
||||
"while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure");
|
||||
try {
|
||||
state.forceAttrs(*e, noPos, "");
|
||||
} catch (Error & err) {
|
||||
err.addTrace(nullptr, "in genericClosure element %s", ValuePrinter(state, *e, errorPrintOptions));
|
||||
throw;
|
||||
}
|
||||
|
||||
auto key = state.getAttr(
|
||||
state.s.key,
|
||||
e->attrs(),
|
||||
"in one of the attrsets generated by (or initially passed to) builtins.genericClosure");
|
||||
const Attr * key;
|
||||
try {
|
||||
key = state.getAttr(state.s.key, e->attrs(), "");
|
||||
} catch (Error & err) {
|
||||
err.addTrace(nullptr, "in genericClosure element %s", ValuePrinter(state, *e, errorPrintOptions));
|
||||
throw;
|
||||
}
|
||||
state.forceValue(*key->value, noPos);
|
||||
|
||||
if (!doneKeys.insert(key->value).second)
|
||||
continue;
|
||||
try {
|
||||
auto [it, inserted] = keyToElem.insert({key->value, e});
|
||||
if (!inserted)
|
||||
continue;
|
||||
} catch (Error & err) {
|
||||
// Try to find which element we're comparing against
|
||||
Value * otherElem = nullptr;
|
||||
for (auto & [otherKey, elem] : keyToElem) {
|
||||
try {
|
||||
cmp(key->value, otherKey);
|
||||
} catch (Error &) {
|
||||
// Found the element we're comparing against
|
||||
otherElem = elem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (otherElem) {
|
||||
// Traces are printed in reverse order; pre-swap them.
|
||||
err.addTrace(nullptr, "with element %s", ValuePrinter(state, *otherElem, errorPrintOptions));
|
||||
err.addTrace(nullptr, "while comparing element %s", ValuePrinter(state, *e, errorPrintOptions));
|
||||
} else {
|
||||
// Couldn't find the specific element, just show current
|
||||
err.addTrace(nullptr, "while checking key of element %s", ValuePrinter(state, *e, errorPrintOptions));
|
||||
}
|
||||
throw;
|
||||
}
|
||||
res.push_back(e);
|
||||
|
||||
/* Call the `operator' function with `e' as argument. */
|
||||
Value newElements;
|
||||
state.callFunction(*op->value, {&e, 1}, newElements, noPos);
|
||||
state.forceList(
|
||||
newElements,
|
||||
noPos,
|
||||
"while evaluating the return value of the `operator` passed to builtins.genericClosure");
|
||||
try {
|
||||
state.callFunction(*op->value, {&e, 1}, newElements, noPos);
|
||||
state.forceList(
|
||||
newElements,
|
||||
noPos,
|
||||
"while evaluating the return value of the `operator` passed to builtins.genericClosure");
|
||||
|
||||
/* Add the values returned by the operator to the work set. */
|
||||
for (auto elem : newElements.listView()) {
|
||||
state.forceValue(*elem, noPos); // "while evaluating one one of the elements returned by the `operator`
|
||||
// passed to builtins.genericClosure");
|
||||
workSet.push_back(elem);
|
||||
/* Add the values returned by the operator to the work set. */
|
||||
for (auto elem : newElements.listView()) {
|
||||
state.forceValue(*elem, noPos); // "while evaluating one one of the elements returned by the `operator`
|
||||
// passed to builtins.genericClosure");
|
||||
workSet.push_back(elem);
|
||||
}
|
||||
} catch (Error & err) {
|
||||
err.addTrace(
|
||||
nullptr,
|
||||
"while calling %s on genericClosure element %s",
|
||||
state.symbols[state.s.operator_],
|
||||
ValuePrinter(state, *e, errorPrintOptions));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -825,10 +873,10 @@ static RegisterPrimOp primop_genericClosure(
|
||||
|
||||
- [Int](@docroot@/language/types.md#type-int)
|
||||
- [Float](@docroot@/language/types.md#type-float)
|
||||
- [Boolean](@docroot@/language/types.md#type-boolean)
|
||||
- [Boolean](@docroot@/language/types.md#type-bool)
|
||||
- [String](@docroot@/language/types.md#type-string)
|
||||
- [Path](@docroot@/language/types.md#type-path)
|
||||
- [List](@docroot@/language/types.md#list)
|
||||
- [List](@docroot@/language/types.md#type-list)
|
||||
|
||||
The result is produced by calling the `operator` on each `item` that has not been called yet, including newly added items, until no new items are added.
|
||||
Items are compared by their `key` attribute.
|
||||
@@ -1974,8 +2022,13 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, Value
|
||||
NixStringContext context;
|
||||
auto path = state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the first argument passed to 'builtins.dirOf'", false, false);
|
||||
auto dir = dirOf(*path);
|
||||
v.mkString(dir, context);
|
||||
auto pos = path->rfind('/');
|
||||
if (pos == path->npos)
|
||||
v.mkStringMove(".", context);
|
||||
else if (pos == 0)
|
||||
v.mkStringMove("/", context);
|
||||
else
|
||||
v.mkString(path->substr(0, pos), context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2103,7 +2156,7 @@ static RegisterPrimOp primop_findFile(
|
||||
builtins.findFile builtins.nixPath "nixpkgs"
|
||||
```
|
||||
|
||||
A search path is represented as a list of [attribute sets](./types.md#attribute-set) with two attributes:
|
||||
A search path is represented as a list of [attribute sets](./types.md#type-attrs) with two attributes:
|
||||
- `prefix` is a relative path.
|
||||
- `path` denotes a file system location
|
||||
|
||||
@@ -2186,7 +2239,7 @@ static RegisterPrimOp primop_findFile(
|
||||
> - ```
|
||||
> {
|
||||
> prefix = "nixpkgs";
|
||||
> path = "https://nixos.org/channels/nixos-unstable/nixexprs.tar.xz";
|
||||
> path = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz";
|
||||
> }
|
||||
> ```
|
||||
|
||||
@@ -2395,7 +2448,7 @@ static RegisterPrimOp primop_outputOf({
|
||||
|
||||
returns an input placeholder for the output of the output of `myDrv`.
|
||||
|
||||
This primop corresponds to the `^` sigil for [deriving paths](@docroot@/glossary.md#gloss-deriving-paths), e.g. as part of installable syntax on the command line.
|
||||
This primop corresponds to the `^` sigil for [deriving paths](@docroot@/glossary.md#gloss-deriving-path), e.g. as part of installable syntax on the command line.
|
||||
)",
|
||||
.fun = prim_outputOf,
|
||||
.experimentalFeature = Xp::DynamicDerivations,
|
||||
@@ -2930,7 +2983,7 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value ** args, V
|
||||
for (const auto & [n, i] : enumerate(*args[0]->attrs()))
|
||||
list[n] = Value::toPtr(state.symbols[i.name]);
|
||||
|
||||
std::sort(list.begin(), list.end(), [](Value * v1, Value * v2) { return strcmp(v1->c_str(), v2->c_str()) < 0; });
|
||||
std::sort(list.begin(), list.end(), [](Value * v1, Value * v2) { return v1->string_view() < v2->string_view(); });
|
||||
|
||||
v.mkList(list);
|
||||
}
|
||||
@@ -4966,7 +5019,7 @@ static RegisterPrimOp primop_compareVersions({
|
||||
version *s1* is older than version *s2*, `0` if they are the same,
|
||||
and `1` if *s1* is newer than *s2*. The version comparison
|
||||
algorithm is the same as the one used by [`nix-env
|
||||
-u`](../command-ref/nix-env.md#operation---upgrade).
|
||||
-u`](../command-ref/nix-env/upgrade.md).
|
||||
)",
|
||||
.fun = prim_compareVersions,
|
||||
});
|
||||
@@ -4995,7 +5048,7 @@ static RegisterPrimOp primop_splitVersion({
|
||||
.doc = R"(
|
||||
Split a string representing a version into its components, by the
|
||||
same version splitting logic underlying the version comparison in
|
||||
[`nix-env -u`](../command-ref/nix-env.md#operation---upgrade).
|
||||
[`nix-env -u`](../command-ref/nix-env/upgrade.md).
|
||||
)",
|
||||
.fun = prim_splitVersion,
|
||||
});
|
||||
@@ -5045,9 +5098,9 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
|
||||
Primitive value.
|
||||
|
||||
It can be returned by
|
||||
[comparison operators](@docroot@/language/operators.md#Comparison)
|
||||
[comparison operators](@docroot@/language/operators.md#comparison)
|
||||
and used in
|
||||
[conditional expressions](@docroot@/language/syntax.md#Conditionals).
|
||||
[conditional expressions](@docroot@/language/syntax.md#conditionals).
|
||||
|
||||
The name `true` is not special, and can be shadowed:
|
||||
|
||||
@@ -5068,9 +5121,9 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
|
||||
Primitive value.
|
||||
|
||||
It can be returned by
|
||||
[comparison operators](@docroot@/language/operators.md#Comparison)
|
||||
[comparison operators](@docroot@/language/operators.md#comparison)
|
||||
and used in
|
||||
[conditional expressions](@docroot@/language/syntax.md#Conditionals).
|
||||
[conditional expressions](@docroot@/language/syntax.md#conditionals).
|
||||
|
||||
The name `false` is not special, and can be shadowed:
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency(
|
||||
Create a copy of the given string where every
|
||||
[derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep)
|
||||
string context element is turned into a
|
||||
[constant](@docroot@/language/string-context.md#string-context-element-constant)
|
||||
[constant](@docroot@/language/string-context.md#string-context-constant)
|
||||
string context element.
|
||||
|
||||
This is the opposite of [`builtins.addDrvOutputDependencies`](#builtins-addDrvOutputDependencies).
|
||||
@@ -145,7 +145,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies(
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
Create a copy of the given string where a single
|
||||
[constant](@docroot@/language/string-context.md#string-context-element-constant)
|
||||
[constant](@docroot@/language/string-context.md#string-context-constant)
|
||||
string context element is turned into a
|
||||
[derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep)
|
||||
string context element.
|
||||
|
||||
@@ -33,7 +33,7 @@ json printValueAsJSON(
|
||||
|
||||
case nString:
|
||||
copyContext(v, context);
|
||||
out = v.c_str();
|
||||
out = v.string_view();
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
|
||||
@@ -82,7 +82,7 @@ static void printValueAsXML(
|
||||
case nString:
|
||||
/* !!! show the context? */
|
||||
copyContext(v, context);
|
||||
doc.writeEmptyElement("string", singletonAttrs("value", v.c_str()));
|
||||
doc.writeEmptyElement("string", singletonAttrs("value", v.string_view()));
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
@@ -102,14 +102,14 @@ static void printValueAsXML(
|
||||
if (strict)
|
||||
state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->c_str();
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->string_view();
|
||||
}
|
||||
|
||||
if (auto a = v.attrs()->get(state.s.outPath)) {
|
||||
if (strict)
|
||||
state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["outPath"] = a->value->c_str();
|
||||
xmlAttrs["outPath"] = a->value->string_view();
|
||||
}
|
||||
|
||||
XMLOpenElement _(doc, "derivation", xmlAttrs);
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#include "nix/fetchers/git-utils.hh"
|
||||
#include "nix/util/file-system.hh"
|
||||
#include <gmock/gmock.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 <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 <gtest/gtest.h>
|
||||
#include "nix/util/fs-sink.hh"
|
||||
#include "nix/util/serialise.hh"
|
||||
#include "nix/fetchers/git-lfs-fetch.hh"
|
||||
|
||||
#include <git2/blob.h>
|
||||
#include <git2/tree.h>
|
||||
#include <git2-experimental/blob.h>
|
||||
#include <git2-experimental/tree.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "nix/fetchers/fetchers.hh"
|
||||
#include "nix/fetchers/git-utils.hh"
|
||||
|
||||
#include <git2.h>
|
||||
#include <git2-experimental.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user