- 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**
>
diff --git a/doc/manual/source/command-ref/nix-env/upgrade.md b/doc/manual/source/command-ref/nix-env/upgrade.md
index 2779363c3..bf4c1a8ed 100644
--- a/doc/manual/source/command-ref/nix-env/upgrade.md
+++ b/doc/manual/source/command-ref/nix-env/upgrade.md
@@ -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.
diff --git a/doc/manual/source/development/building.md b/doc/manual/source/development/building.md
index 889d81d80..eb65a7247 100644
--- a/doc/manual/source/development/building.md
+++ b/doc/manual/source/development/building.md
@@ -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.
diff --git a/doc/manual/source/development/testing.md b/doc/manual/source/development/testing.md
index c0b130155..7c2cbbb5d 100644
--- a/doc/manual/source/development/testing.md
+++ b/doc/manual/source/development/testing.md
@@ -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.
diff --git a/doc/manual/source/glossary.md b/doc/manual/source/glossary.md
index e6a294e7d..502e6d4de 100644
--- a/doc/manual/source/glossary.md
+++ b/doc/manual/source/glossary.md
@@ -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}
diff --git a/doc/manual/source/language/advanced-attributes.md b/doc/manual/source/language/advanced-attributes.md
index c9d64f060..f0b1a4c73 100644
--- a/doc/manual/source/language/advanced-attributes.md
+++ b/doc/manual/source/language/advanced-attributes.md
@@ -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].
diff --git a/doc/manual/source/language/derivations.md b/doc/manual/source/language/derivations.md
index 43eec680b..2403183fc 100644
--- a/doc/manual/source/language/derivations.md
+++ b/doc/manual/source/language/derivations.md
@@ -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
diff --git a/doc/manual/source/language/identifiers.md b/doc/manual/source/language/identifiers.md
index 584a2f861..67bb1eeec 100644
--- a/doc/manual/source/language/identifiers.md
+++ b/doc/manual/source/language/identifiers.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**
>
diff --git a/doc/manual/source/language/index.md b/doc/manual/source/language/index.md
index 1eb14e96d..116f928dc 100644
--- a/doc/manual/source/language/index.md
+++ b/doc/manual/source/language/index.md
@@ -137,7 +137,7 @@ This is an incomplete overview of language features, by example.
|
- [Booleans](@docroot@/language/types.md#type-boolean)
+ [Booleans](@docroot@/language/types.md#type-bool)
|
@@ -245,7 +245,7 @@ This is an incomplete overview of language features, by example.
- 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`
|
@@ -285,7 +285,7 @@ This is an incomplete overview of language features, by example.
- [Lists](@docroot@/language/types.md#list) with three elements.
+ [Lists](@docroot@/language/types.md#type-list) with three elements.
|
@@ -369,7 +369,7 @@ This is an incomplete overview of language features, by example.
- [Attribute selection](@docroot@/language/types.md#attribute-set) (evaluates to `1`)
+ [Attribute selection](@docroot@/language/types.md#type-attrs) (evaluates to `1`)
|
@@ -381,7 +381,7 @@ This is an incomplete overview of language features, by example.
- [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`)
|
diff --git a/doc/manual/source/language/string-context.md b/doc/manual/source/language/string-context.md
index 0d8fcdefa..65c59d865 100644
--- a/doc/manual/source/language/string-context.md
+++ b/doc/manual/source/language/string-context.md
@@ -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
diff --git a/doc/manual/source/language/string-interpolation.md b/doc/manual/source/language/string-interpolation.md
index a503d5f04..8e25d2b63 100644
--- a/doc/manual/source/language/string-interpolation.md
+++ b/doc/manual/source/language/string-interpolation.md
@@ -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**
>
diff --git a/doc/manual/source/language/syntax.md b/doc/manual/source/language/syntax.md
index 85162db74..b127aca14 100644
--- a/doc/manual/source/language/syntax.md
+++ b/doc/manual/source/language/syntax.md
@@ -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:
diff --git a/doc/manual/source/protocols/json/build-result.md b/doc/manual/source/protocols/json/build-result.md
new file mode 100644
index 000000000..527e7bcc0
--- /dev/null
+++ b/doc/manual/source/protocols/json/build-result.md
@@ -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}}
+```
\ No newline at end of file
diff --git a/doc/manual/source/protocols/json/build-trace-entry.md b/doc/manual/source/protocols/json/build-trace-entry.md
new file mode 100644
index 000000000..8050a2840
--- /dev/null
+++ b/doc/manual/source/protocols/json/build-trace-entry.md
@@ -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}}
+```
+
+
\ No newline at end of file
diff --git a/doc/manual/source/protocols/json/derivation.md b/doc/manual/source/protocols/json/derivation.md
index a4a4ea79d..6eafb255e 100644
--- a/doc/manual/source/protocols/json/derivation.md
+++ b/doc/manual/source/protocols/json/derivation.md
@@ -1,7 +1,7 @@
-{{#include derivation-v3-fixed.md}}
+{{#include derivation-v4-fixed.md}}
diff --git a/doc/manual/source/protocols/json/meson.build b/doc/manual/source/protocols/json/meson.build
index 7ebcff697..4ab94c63b 100644
--- a/doc/manual/source/protocols/json/meson.build
+++ b/doc/manual/source/protocols/json/meson.build
@@ -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()
diff --git a/doc/manual/source/protocols/json/schema/build-result-v1 b/doc/manual/source/protocols/json/schema/build-result-v1
new file mode 120000
index 000000000..a143d2c50
--- /dev/null
+++ b/doc/manual/source/protocols/json/schema/build-result-v1
@@ -0,0 +1 @@
+../../../../../../src/libstore-tests/data/build-result
\ No newline at end of file
diff --git a/doc/manual/source/protocols/json/schema/build-result-v1.yaml b/doc/manual/source/protocols/json/schema/build-result-v1.yaml
new file mode 100644
index 000000000..31f59a44d
--- /dev/null
+++ b/doc/manual/source/protocols/json/schema/build-result-v1.yaml
@@ -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.
diff --git a/doc/manual/source/protocols/json/schema/build-trace-entry-v1 b/doc/manual/source/protocols/json/schema/build-trace-entry-v1
new file mode 120000
index 000000000..0d02880a5
--- /dev/null
+++ b/doc/manual/source/protocols/json/schema/build-trace-entry-v1
@@ -0,0 +1 @@
+../../../../../../src/libstore-tests/data/realisation
\ No newline at end of file
diff --git a/doc/manual/source/protocols/json/schema/build-trace-entry-v1.yaml b/doc/manual/source/protocols/json/schema/build-trace-entry-v1.yaml
new file mode 100644
index 000000000..cabf2c350
--- /dev/null
+++ b/doc/manual/source/protocols/json/schema/build-trace-entry-v1.yaml
@@ -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
diff --git a/doc/manual/source/protocols/json/schema/derivation-v3.yaml b/doc/manual/source/protocols/json/schema/derivation-v4.yaml
similarity index 78%
rename from doc/manual/source/protocols/json/schema/derivation-v3.yaml
rename to doc/manual/source/protocols/json/schema/derivation-v4.yaml
index fa68adcb1..2528f7502 100644
--- a/doc/manual/source/protocols/json/schema/derivation-v3.yaml
+++ b/doc/manual/source/protocols/json/schema/derivation-v4.yaml
@@ -1,8 +1,8 @@
"$schema": "http://json-schema.org/draft-04/schema"
-"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/derivation-v3.json"
+"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/derivation-v4.json"
title: Derivation
description: |
- Experimental JSON representation of a Nix derivation (version 3).
+ Experimental JSON representation of a Nix derivation (version 4).
This schema describes the JSON representation of Nix's `Derivation` type.
@@ -17,8 +17,7 @@ required:
- name
- version
- outputs
- - inputSrcs
- - inputDrvs
+ - inputs
- system
- builder
- args
@@ -32,10 +31,10 @@ properties:
Used when calculating store paths for the derivation’s outputs.
version:
- const: 3
- title: Format version (must be 3)
+ const: 4
+ title: Format version (must be 4)
description: |
- Must be `3`.
+ 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:
@@ -47,6 +46,12 @@ properties:
- 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:
@@ -70,47 +75,56 @@ properties:
additionalProperties:
"$ref": "#/$defs/output/overall"
- 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:
+ inputs:
type: object
- title: Input derivations
+ title: Derivation inputs
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
+ Input dependencies for the derivation, organized into source paths and derivation dependencies.
+ required:
+ - srcs
+ - drvs
+ properties:
+ srcs:
+ type: array
+ title: Input source paths
description: |
- A store path to a derivation, mapped to the outputs of that derivation.
- oneOf:
- - "$ref": "#/$defs/outputNames"
- - "$ref": "#/$defs/dynamicOutputs"
+ 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:
@@ -189,24 +203,18 @@ properties:
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.
- type: object
+ "$ref": "./content-address-v1.yaml"
required:
- method
- - hashAlgo
- hash
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"
hash:
- type: string
title: Expected hash value
description: |
- The expected content hash in base-16.
+ The expected content hash.
additionalProperties: false
caFloating:
diff --git a/doc/manual/source/protocols/json/schema/hash-v1.yaml b/doc/manual/source/protocols/json/schema/hash-v1.yaml
index 316fb6d73..821546dee 100644
--- a/doc/manual/source/protocols/json/schema/hash-v1.yaml
+++ b/doc/manual/source/protocols/json/schema/hash-v1.yaml
@@ -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.
diff --git a/doc/manual/source/protocols/json/schema/store-object-info-v1 b/doc/manual/source/protocols/json/schema/store-object-info-v2
similarity index 100%
rename from doc/manual/source/protocols/json/schema/store-object-info-v1
rename to doc/manual/source/protocols/json/schema/store-object-info-v2
diff --git a/doc/manual/source/protocols/json/schema/store-object-info-v1.yaml b/doc/manual/source/protocols/json/schema/store-object-info-v2.yaml
similarity index 91%
rename from doc/manual/source/protocols/json/schema/store-object-info-v1.yaml
rename to doc/manual/source/protocols/json/schema/store-object-info-v2.yaml
index d79f25043..4f442e0c3 100644
--- a/doc/manual/source/protocols/json/schema/store-object-info-v1.yaml
+++ b/doc/manual/source/protocols/json/schema/store-object-info-v2.yaml
@@ -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" }
diff --git a/doc/manual/source/protocols/json/store-object-info.md b/doc/manual/source/protocols/json/store-object-info.md
index 4673dd773..6a101ab0f 100644
--- a/doc/manual/source/protocols/json/store-object-info.md
+++ b/doc/manual/source/protocols/json/store-object-info.md
@@ -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 @@
diff --git a/doc/manual/source/protocols/nix-archive/index.md b/doc/manual/source/protocols/nix-archive/index.md
index 4d25f63e2..bd2a8e833 100644
--- a/doc/manual/source/protocols/nix-archive/index.md
+++ b/doc/manual/source/protocols/nix-archive/index.md
@@ -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.
diff --git a/doc/manual/source/release-notes/rl-2.18.md b/doc/manual/source/release-notes/rl-2.18.md
index eb26fc9e7..71b25f408 100644
--- a/doc/manual/source/release-notes/rl-2.18.md
+++ b/doc/manual/source/release-notes/rl-2.18.md
@@ -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.
diff --git a/doc/manual/source/release-notes/rl-2.19.md b/doc/manual/source/release-notes/rl-2.19.md
index 06c704324..04f8c9c28 100644
--- a/doc/manual/source/release-notes/rl-2.19.md
+++ b/doc/manual/source/release-notes/rl-2.19.md
@@ -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:
diff --git a/doc/manual/source/release-notes/rl-2.23.md b/doc/manual/source/release-notes/rl-2.23.md
index e6b0e9ffc..b358a0fdc 100644
--- a/doc/manual/source/release-notes/rl-2.23.md
+++ b/doc/manual/source/release-notes/rl-2.23.md
@@ -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.
diff --git a/doc/manual/source/release-notes/rl-2.24.md b/doc/manual/source/release-notes/rl-2.24.md
index d4af3cb51..e9b46bb22 100644
--- a/doc/manual/source/release-notes/rl-2.24.md
+++ b/doc/manual/source/release-notes/rl-2.24.md
@@ -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).
diff --git a/doc/manual/source/store/build-trace.md b/doc/manual/source/store/build-trace.md
index 1086dcb88..a879d37d2 100644
--- a/doc/manual/source/store/build-trace.md
+++ b/doc/manual/source/store/build-trace.md
@@ -29,7 +29,7 @@ And even in that case, a different result doesn't mean the original entry was a
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 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.
@@ -40,13 +40,13 @@ 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\\).
+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.
+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
diff --git a/doc/manual/source/store/building.md b/doc/manual/source/store/building.md
index dbfe6b5ca..f2d470e99 100644
--- a/doc/manual/source/store/building.md
+++ b/doc/manual/source/store/building.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:
diff --git a/doc/manual/source/store/derivation/index.md b/doc/manual/source/store/derivation/index.md
index 61c5335ff..670f3b2bd 100644
--- a/doc/manual/source/store/derivation/index.md
+++ b/doc/manual/source/store/derivation/index.md
@@ -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].
diff --git a/doc/manual/source/store/derivation/outputs/index.md b/doc/manual/source/store/derivation/outputs/index.md
index 0683f5703..ca2ce6665 100644
--- a/doc/manual/source/store/derivation/outputs/index.md
+++ b/doc/manual/source/store/derivation/outputs/index.md
@@ -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
diff --git a/doc/manual/source/store/store-object/content-address.md b/doc/manual/source/store/store-object/content-address.md
index 36e841fa3..7834ac510 100644
--- a/doc/manual/source/store/store-object/content-address.md
+++ b/doc/manual/source/store/store-object/content-address.md
@@ -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.
diff --git a/doc/manual/source/store/store-path.md b/doc/manual/source/store/store-path.md
index beec2389b..4061f3653 100644
--- a/doc/manual/source/store/store-path.md
+++ b/doc/manual/source/store/store-path.md
@@ -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.
diff --git a/flake.nix b/flake.nix
index a70617b74..08f518983 100644
--- a/flake.nix
+++ b/flake.nix
@@ -485,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";
diff --git a/meson.build b/meson.build
index c493dfad6..f3158ea6d 100644
--- a/meson.build
+++ b/meson.build
@@ -61,4 +61,3 @@ if get_option('unit-tests')
endif
subproject('nix-functional-tests')
subproject('json-schema-checks')
-subproject('kaitai-struct-checks')
diff --git a/packaging/dev-shell.nix b/packaging/dev-shell.nix
index ea12e079f..153e7a3eb 100644
--- a/packaging/dev-shell.nix
+++ b/packaging/dev-shell.nix
@@ -109,7 +109,6 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
++ pkgs.nixComponents2.nix-external-api-docs.nativeBuildInputs
++ pkgs.nixComponents2.nix-functional-tests.externalNativeBuildInputs
++ pkgs.nixComponents2.nix-json-schema-checks.externalNativeBuildInputs
- ++ pkgs.nixComponents2.nix-kaitai-struct-checks.externalNativeBuildInputs
++ lib.optional (
!buildCanExecuteHost
# Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479
@@ -149,7 +148,6 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
++ pkgs.nixComponents2.nix-expr.externalPropagatedBuildInputs
++ pkgs.nixComponents2.nix-cmd.buildInputs
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.externalBuildInputs
- ++ lib.optional havePerl pkgs.perl
- ++ pkgs.nixComponents2.nix-kaitai-struct-checks.externalBuildInputs;
+ ++ lib.optional havePerl pkgs.perl;
}
)
diff --git a/src/json-schema-checks/build-result b/src/json-schema-checks/build-result
new file mode 120000
index 000000000..8010d0fdd
--- /dev/null
+++ b/src/json-schema-checks/build-result
@@ -0,0 +1 @@
+../../src/libstore-tests/data/build-result
\ No newline at end of file
diff --git a/src/json-schema-checks/build-trace-entry b/src/json-schema-checks/build-trace-entry
new file mode 120000
index 000000000..9175e750e
--- /dev/null
+++ b/src/json-schema-checks/build-trace-entry
@@ -0,0 +1 @@
+../../src/libstore-tests/data/realisation
\ No newline at end of file
diff --git a/src/json-schema-checks/meson.build b/src/json-schema-checks/meson.build
index 67f553162..f72affb0b 100644
--- a/src/json-schema-checks/meson.build
+++ b/src/json-schema-checks/meson.build
@@ -54,6 +54,15 @@ schemas = [
'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
@@ -61,7 +70,7 @@ 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',
@@ -69,7 +78,7 @@ schemas += [
},
{
'stem' : 'derivation',
- 'schema' : schema_dir / 'derivation-v3.yaml#/$defs/output/overall',
+ 'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/overall',
'files' : [
'output-caFixedFlat.json',
'output-caFixedNAR.json',
@@ -83,14 +92,14 @@ schemas += [
# Match exact variant
{
'stem' : 'derivation',
- 'schema' : schema_dir / 'derivation-v3.yaml#/$defs/output/inputAddressed',
+ 'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/inputAddressed',
'files' : [
'output-inputAddressed.json',
],
},
{
'stem' : 'derivation',
- 'schema' : schema_dir / 'derivation-v3.yaml#/$defs/output/caFixed',
+ 'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/caFixed',
'files' : [
'output-caFixedFlat.json',
'output-caFixedNAR.json',
@@ -99,21 +108,21 @@ schemas += [
},
{
'stem' : 'derivation',
- 'schema' : schema_dir / 'derivation-v3.yaml#/$defs/output/caFloating',
+ 'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/caFloating',
'files' : [
'output-caFloating.json',
],
},
{
'stem' : 'derivation',
- 'schema' : schema_dir / 'derivation-v3.yaml#/$defs/output/deferred',
+ 'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/deferred',
'files' : [
'output-deferred.json',
],
},
{
'stem' : 'derivation',
- 'schema' : schema_dir / 'derivation-v3.yaml#/$defs/output/impure',
+ 'schema' : schema_dir / 'derivation-v4.yaml#/$defs/output/impure',
'files' : [
'output-impure.json',
],
@@ -125,7 +134,7 @@ 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',
@@ -135,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',
@@ -152,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',
@@ -160,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',
],
diff --git a/src/json-schema-checks/package.nix b/src/json-schema-checks/package.nix
index 160db003f..5365fe75e 100644
--- a/src/json-schema-checks/package.nix
+++ b/src/json-schema-checks/package.nix
@@ -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
./.
];
diff --git a/src/kaitai-struct-checks/package.nix b/src/kaitai-struct-checks/package.nix
index 263dd6fd1..97d56aabd 100644
--- a/src/kaitai-struct-checks/package.nix
+++ b/src/kaitai-struct-checks/package.nix
@@ -1,4 +1,5 @@
# Run with: nix build .#nix-kaitai-struct-checks
+# or: `nix develop .#nix-kaitai-struct-checks` to enter a dev shell
{
lib,
mkMesonDerivation,
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 887b2946f..0372d6cc1 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -136,17 +136,19 @@ struct AttrDb
});
}
- AttrId setString(AttrKey key, std::string_view s, Value::StringWithContext::Context 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 (Value::StringWithContext::Context 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();
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index f25a72511..47880b9c5 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -821,15 +821,18 @@ void Value::mkString(std::string_view s)
mkStringNoCopy(makeImmutableString(s));
}
-static Value::StringWithContext::Context encodeContext(const NixStringContext & context)
+Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
{
if (!context.empty()) {
- size_t n = 0;
- auto ctx = (Value::StringWithContext::Context) allocBytes((context.size() + 1) * sizeof(char *));
- for (auto & i : context) {
- ctx[n++] = makeImmutableString({i.to_string()});
+ auto ctx = (Value::StringWithContext::Context *) allocBytes(sizeof(size_t) + context.size() * sizeof(char *));
+ ctx->size = context.size();
+ /* Mapping the original iterator to turn references into
+ pointers is necessary to make sure that enumerate doesn't
+ accidently copy the elements when it returns tuples by value.
+ */
+ for (auto [n, i] : enumerate(context | std::views::transform([](const auto & r) { return &r; }))) {
+ ctx->elems[n] = makeImmutableString({i->to_string()});
}
- ctx[n] = nullptr;
return ctx;
} else
return nullptr;
@@ -837,12 +840,12 @@ static Value::StringWithContext::Context encodeContext(const NixStringContext &
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)
@@ -2287,9 +2290,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 (Value::StringWithContext::Context 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 +2312,9 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
auto s = forceString(v, pos, errorCtx);
if (v.context()) {
error(
- "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();
}
@@ -3216,8 +3221,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);
diff --git a/src/libexpr/include/nix/expr/nixexpr.hh b/src/libexpr/include/nix/expr/nixexpr.hh
index fd7ed2a67..08d39cd87 100644
--- a/src/libexpr/include/nix/expr/nixexpr.hh
+++ b/src/libexpr/include/nix/expr/nixexpr.hh
@@ -91,13 +91,6 @@ std::string showAttrPath(const SymbolTable & symbols, std::span
using UpdateQueue = SmallTemporaryValueVector;
-class Exprs
-{
- std::pmr::monotonic_buffer_resource buffer;
-public:
- std::pmr::polymorphic_allocator 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> es;
- ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector> && es)
+ std::span> es;
+
+ ExprConcatStrings(
+ std::pmr::polymorphic_allocator & alloc,
+ const PosIdx & pos,
+ bool forceString,
+ const std::vector> & es)
: pos(pos)
, forceString(forceString)
- , es(std::move(es)) {};
+ , es({alloc.allocate_object>(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 alloc{&buffer};
+
+ template
+ [[gnu::always_inline]]
+ C * add(auto &&... args)
+ {
+ return alloc.new_object(std::forward(args)...);
+ }
+
+ // we define some calls to add explicitly so that the argument can be passed in as initializer lists
+ template
+ [[gnu::always_inline]]
+ C * add(const PosIdx & pos, Expr * fun, std::vector && args)
+ requires(std::same_as)
+ {
+ return alloc.new_object(pos, fun, std::move(args));
+ }
+
+ template
+ [[gnu::always_inline]]
+ C * add(const PosIdx & pos, Expr * fun, std::vector && args, PosIdx && cursedOrEndPos)
+ requires(std::same_as)
+ {
+ return alloc.new_object(pos, fun, std::move(args), std::move(cursedOrEndPos));
+ }
+
+ template
+ [[gnu::always_inline]]
+ C *
+ add(std::pmr::polymorphic_allocator & alloc,
+ const PosIdx & pos,
+ bool forceString,
+ const std::vector> & es)
+ requires(std::same_as)
+ {
+ return alloc.new_object(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. */
diff --git a/src/libexpr/include/nix/expr/parser-state.hh b/src/libexpr/include/nix/expr/parser-state.hh
index 4cffaa497..89c424f82 100644
--- a/src/libexpr/include/nix/expr/parser-state.hh
+++ b/src/libexpr/include/nix/expr/parser-state.hh
@@ -78,7 +78,7 @@ struct LexerState
struct ParserState
{
const LexerState & lexerState;
- std::pmr::polymorphic_allocator & 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();
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
}
} else {
- nested = new ExprAttrs;
+ nested = exprs.add();
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>> && es)
{
if (es.empty())
- return new ExprString("");
+ return exprs.add("");
/* 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::vectorfirst, new ExprString(alloc, s2));
+ es2.emplace_back(i->first, exprs.add(exprs.alloc, s2));
}
};
for (; i != es.end(); ++i, --n) {
@@ -332,7 +332,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector("");
return result;
}
@@ -341,7 +341,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector(exprs.alloc, pos, true, std::move(es2));
}
inline PosIdx LexerState::at(const ParserLocation & loc)
diff --git a/src/libexpr/include/nix/expr/value.hh b/src/libexpr/include/nix/expr/value.hh
index 65204b642..c3a45d4d2 100644
--- a/src/libexpr/include/nix/expr/value.hh
+++ b/src/libexpr/include/nix/expr/value.hh
@@ -220,18 +220,55 @@ struct ValueBase
struct StringWithContext
{
const char * c_str;
+
/**
* The type of the context itself.
*
- * Currently, it is pointer to an array of pointers to strings.
- * The strings are specially formatted to represent a flattening
- * of the recursive sum type that is s context element.
+ * 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.
*/
- using Context = const char **;
- Context context; // must be in sorted order
+ struct Context
+ {
+ using Elem = const char *;
+
+ /**
+ * Number of items in the array
+ */
+ size_t size;
+
+ private:
+ /**
+ * must be in sorted order
+ */
+ Elem elems[/*size*/];
+ public:
+
+ const Elem * begin() const
+ {
+ return elems;
+ }
+
+ const Elem * end() const
+ {
+ return &elems[size];
+ }
+
+ /**
+ * @note returns a null pointer to more concisely encode the
+ * empty context
+ */
+ static Context * fromBuilder(const NixStringContext & context);
+ };
+
+ /**
+ * May be null for a string without context.
+ */
+ const Context * context;
};
struct Path
@@ -1002,7 +1039,7 @@ public:
setStorage(b);
}
- void mkStringNoCopy(const char * s, Value::StringWithContext::Context context = 0) noexcept
+ void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept
{
setStorage(StringWithContext{.c_str = s, .context = context});
}
@@ -1128,7 +1165,7 @@ public:
return getStorage().c_str;
}
- Value::StringWithContext::Context context() const noexcept
+ const Value::StringWithContext::Context * context() const noexcept
{
return getStorage().context;
}
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index a1d1b7e4b..db5e52c3a 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -523,6 +523,7 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr &
prevWith = 0;
for (curEnv = env.get(), level = 1; curEnv; curEnv = curEnv->up.get(), level++)
if (curEnv->isWith) {
+ assert(level <= std::numeric_limits::max());
prevWith = level;
break;
}
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 29586ed98..8b56eba15 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -63,7 +63,7 @@ Expr * parseExprFromBuf(
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
- std::pmr::polymorphic_allocator & 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(fn)) {
e2->args.push_back(arg);
return fn;
}
- return new ExprCall(pos, fn, {arg});
+ return exprs.add(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(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(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(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(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(CUR_POS, $2, $4); }
| WITH expr ';' expr_function
- { $$ = new ExprWith(CUR_POS, $2, $4); }
+ { $$ = state->exprs.add(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($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(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($2); }
+ | '-' expr_op %prec NEGATE { $$ = state->exprs.add(CUR_POS, state->exprs.add(state->s.sub), {state->exprs.add(0), $2}); }
+ | expr_op EQ expr_op { $$ = state->exprs.add($1, $3); }
+ | expr_op NEQ expr_op { $$ = state->exprs.add($1, $3); }
+ | expr_op '<' expr_op { $$ = state->exprs.add(state->at(@2), state->exprs.add(state->s.lessThan), {$1, $3}); }
+ | expr_op LEQ expr_op { $$ = state->exprs.add(state->exprs.add(state->at(@2), state->exprs.add(state->s.lessThan), {$3, $1})); }
+ | expr_op '>' expr_op { $$ = state->exprs.add(state->at(@2), state->exprs.add(state->s.lessThan), {$3, $1}); }
+ | expr_op GEQ expr_op { $$ = state->exprs.add(state->exprs.add(state->at(@2), state->exprs.add(state->s.lessThan), {$1, $3})); }
+ | expr_op AND expr_op { $$ = state->exprs.add(state->at(@2), $1, $3); }
+ | expr_op OR expr_op { $$ = state->exprs.add(state->at(@2), $1, $3); }
+ | expr_op IMPL expr_op { $$ = state->exprs.add(state->at(@2), $1, $3); }
+ | expr_op UPDATE expr_op { $$ = state->exprs.add(state->at(@2), $1, $3); }
+ | expr_op '?' attrpath { $$ = state->exprs.add(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(state->exprs.alloc, state->at(@2), false, {{state->at(@1), $1}, {state->at(@3), $3}}); }
+ | expr_op '-' expr_op { $$ = state->exprs.add(state->at(@2), state->exprs.add(state->s.sub), {$1, $3}); }
+ | expr_op '*' expr_op { $$ = state->exprs.add(state->at(@2), state->exprs.add(state->s.mul), {$1, $3}); }
+ | expr_op '/' expr_op { $$ = state->exprs.add(state->at(@2), state->exprs.add(state->s.div), {$1, $3}); }
+ | expr_op CONCAT expr_op { $$ = state->exprs.add(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(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(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(CUR_POS, $1, {state->exprs.add(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(CUR_POS);
else
- $$ = new ExprVar(CUR_POS, state->symbols.create($1));
+ $$ = state->exprs.add(CUR_POS, state->symbols.create($1));
}
- | INT_LIT { $$ = new ExprInt($1); }
- | FLOAT_LIT { $$ = new ExprFloat($1); }
+ | INT_LIT { $$ = state->exprs.add($1); }
+ | FLOAT_LIT { $$ = state->exprs.add($1); }
| '"' string_parts '"' {
std::visit(overloaded{
- [&](std::string_view str) { $$ = new ExprString(state->alloc, str); },
+ [&](std::string_view str) { $$ = state->exprs.add(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(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(CUR_POS,
+ state->exprs.add(state->s.findFile),
+ {state->exprs.add(state->s.nixPath),
+ state->exprs.add(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(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(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(CUR_POS); }
+ | '[' list ']' { $$ = state->exprs.add(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(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(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(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(state->exprs.alloc, state->rootFS, path)
+ : state->exprs.add(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(state->rootFS), path);
+ $$ = state->exprs.add(state->exprs.alloc, ref(state->rootFS), path);
}
;
@@ -402,7 +402,7 @@ ind_string_parts
binds
: binds1
- | { $$ = new ExprAttrs; }
+ | { $$ = state->exprs.add(); }
;
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(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(state->exprs.alloc, iPos, from, i.symbol),
iPos,
ExprAttrs::AttrDef::Kind::InheritedFrom));
}
}
| attrpath '=' expr ';'
- { $$ = new ExprAttrs;
+ { $$ = state->exprs.add();
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 & 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,
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 96e79fedd..d1aae64fa 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -825,10 +825,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.
@@ -2103,7 +2103,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
@@ -2395,7 +2395,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,
@@ -4966,7 +4966,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 +4995,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 +5045,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 +5068,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:
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index 12b8ffdf9..8a9fe42e8 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -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.
diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc
index 65587b43a..1bd5b0b94 100644
--- a/src/libfetchers/git-utils.cc
+++ b/src/libfetchers/git-utils.cc
@@ -10,6 +10,8 @@
#include "nix/util/fs-sink.hh"
#include "nix/util/sync.hh"
#include "nix/util/util.hh"
+#include "nix/util/thread-pool.hh"
+#include "nix/util/pool.hh"
#include
#include
@@ -33,12 +35,14 @@
#include
#include
+#include
#include
#include
#include
#include
#include
#include
+#include
namespace std {
@@ -227,12 +231,16 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this
{
/** Location of the repository on disk. */
std::filesystem::path path;
+
+ bool bare;
+
/**
* libgit2 repository. Note that new objects are not written to disk,
* because we are using a mempack backend. For writing to disk, see
* `flush()`, which is also called by `GitFileSystemObjectSink::sync()`.
*/
Repository repo;
+
/**
* In-memory object store for efficient batched writing to packfiles.
* Owned by `repo`.
@@ -241,6 +249,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this
GitRepoImpl(std::filesystem::path _path, bool create, bool bare)
: path(std::move(_path))
+ , bare(bare)
{
initLibGit2();
@@ -317,32 +326,56 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this
checkInterrupt();
}
+ /**
+ * Return a connection pool for this repo. Useful for
+ * multithreaded access.
+ */
+ Pool getPool()
+ {
+ // TODO: as an optimization, it would be nice to include `this` in the pool.
+ return Pool(std::numeric_limits::max(), [this]() -> ref {
+ return make_ref(path, false, bare);
+ });
+ }
+
uint64_t getRevCount(const Hash & rev) override
{
- boost::unordered_flat_set> done;
- std::queue todo;
+ boost::concurrent_flat_set> done;
- todo.push(peelObject(lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT));
+ auto startCommit = peelObject(lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT);
+ auto startOid = *git_commit_id(startCommit.get());
+ done.insert(startOid);
- while (auto commit = pop(todo)) {
- if (!done.insert(*git_commit_id(commit->get())).second)
- continue;
+ auto repoPool(getPool());
- for (size_t n = 0; n < git_commit_parentcount(commit->get()); ++n) {
- git_commit * parent;
- if (git_commit_parent(&parent, commit->get(), n)) {
+ ThreadPool pool;
+
+ auto process = [&done, &pool, &repoPool](this const auto & process, const git_oid & oid) -> void {
+ auto repo(repoPool.get());
+
+ auto _commit = lookupObject(*repo, oid, GIT_OBJECT_COMMIT);
+ auto commit = (const git_commit *) &*_commit;
+
+ for (auto n : std::views::iota(0U, git_commit_parentcount(commit))) {
+ auto parentOid = git_commit_parent_id(commit, n);
+ if (!parentOid) {
throw Error(
"Failed to retrieve the parent of Git commit '%s': %s. "
"This may be due to an incomplete repository history. "
"To resolve this, either enable the shallow parameter in your flake URL (?shallow=1) "
"or add set the shallow parameter to true in builtins.fetchGit, "
"or fetch the complete history for this branch.",
- *git_commit_id(commit->get()),
+ *git_commit_id(commit),
git_error_last()->message);
}
- todo.push(Commit(parent));
+ if (done.insert(*parentOid))
+ pool.enqueue(std::bind(process, *parentOid));
}
- }
+ };
+
+ pool.enqueue(std::bind(process, startOid));
+
+ pool.process();
return done.size();
}
@@ -549,25 +582,15 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this
// then use code that was removed in this commit (see blame)
auto dir = this->path;
- Strings gitArgs{"-C", dir.string(), "--git-dir", ".", "fetch", "--quiet", "--force"};
+ Strings gitArgs{"-C", dir.string(), "--git-dir", ".", "fetch", "--progress", "--force"};
if (shallow)
append(gitArgs, {"--depth", "1"});
append(gitArgs, {std::string("--"), url, refspec});
- auto [status, output] = runProgram(
- RunOptions{
- .program = "git",
- .lookupPath = true,
- // FIXME: git stderr messes up our progress indicator, so
- // we're using --quiet for now. Should process its stderr.
- .args = gitArgs,
- .input = {},
- .mergeStderrToStdout = true,
- .isInteractive = true});
+ auto status = runProgram(RunOptions{.program = "git", .args = gitArgs, .isInteractive = true}).first;
- if (status > 0) {
- throw Error("Failed to fetch git repository %s : %s", url, output);
- }
+ if (status > 0)
+ throw Error("Failed to fetch git repository '%s'", url);
}
void verifyCommit(const Hash & rev, const std::vector & publicKeys) override
diff --git a/src/libfetchers/include/nix/fetchers/registry.hh b/src/libfetchers/include/nix/fetchers/registry.hh
index 90fc3d853..f705f709d 100644
--- a/src/libfetchers/include/nix/fetchers/registry.hh
+++ b/src/libfetchers/include/nix/fetchers/registry.hh
@@ -2,6 +2,7 @@
///@file
#include "nix/util/types.hh"
+#include "nix/util/source-path.hh"
#include "nix/fetchers/fetchers.hh"
namespace nix {
@@ -39,7 +40,7 @@ struct Registry
{
}
- static std::shared_ptr read(const Settings & settings, const Path & path, RegistryType type);
+ static std::shared_ptr read(const Settings & settings, const SourcePath & path, RegistryType type);
void write(const Path & path);
diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc
index e570fc84b..2b0b5f390 100644
--- a/src/libfetchers/registry.cc
+++ b/src/libfetchers/registry.cc
@@ -10,18 +10,18 @@
namespace nix::fetchers {
-std::shared_ptr Registry::read(const Settings & settings, const Path & path, RegistryType type)
+std::shared_ptr Registry::read(const Settings & settings, const SourcePath & path, RegistryType type)
{
debug("reading registry '%s'", path);
auto registry = std::make_shared(settings, type);
- if (!pathExists(path))
+ if (!path.pathExists())
return std::make_shared(settings, type);
try {
- auto json = nlohmann::json::parse(readFile(path));
+ auto json = nlohmann::json::parse(path.readFile());
auto version = json.value("version", 0);
@@ -97,7 +97,10 @@ static Path getSystemRegistryPath()
static std::shared_ptr getSystemRegistry(const Settings & settings)
{
- static auto systemRegistry = Registry::read(settings, getSystemRegistryPath(), Registry::System);
+ static auto systemRegistry = Registry::read(
+ settings,
+ SourcePath{getFSSourceAccessor(), CanonPath{getSystemRegistryPath()}}.resolveSymlinks(),
+ Registry::System);
return systemRegistry;
}
@@ -108,13 +111,17 @@ Path getUserRegistryPath()
std::shared_ptr getUserRegistry(const Settings & settings)
{
- static auto userRegistry = Registry::read(settings, getUserRegistryPath(), Registry::User);
+ static auto userRegistry = Registry::read(
+ settings,
+ SourcePath{getFSSourceAccessor(), CanonPath{getUserRegistryPath()}}.resolveSymlinks(),
+ Registry::User);
return userRegistry;
}
std::shared_ptr getCustomRegistry(const Settings & settings, const Path & p)
{
- static auto customRegistry = Registry::read(settings, p, Registry::Custom);
+ static auto customRegistry =
+ Registry::read(settings, SourcePath{getFSSourceAccessor(), CanonPath{p}}.resolveSymlinks(), Registry::Custom);
return customRegistry;
}
@@ -137,14 +144,19 @@ static std::shared_ptr getGlobalRegistry(const Settings & settings, re
return std::make_shared(settings, Registry::Global); // empty registry
}
- if (!isAbsolute(path)) {
- auto storePath = downloadFile(store, settings, path, "flake-registry.json").storePath;
- if (auto store2 = store.dynamic_pointer_cast())
- store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json");
- path = store->toRealPath(storePath);
- }
-
- return Registry::read(settings, path, Registry::Global);
+ return Registry::read(
+ settings,
+ [&] -> SourcePath {
+ if (!isAbsolute(path)) {
+ auto storePath = downloadFile(store, settings, path, "flake-registry.json").storePath;
+ if (auto store2 = store.dynamic_pointer_cast())
+ store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json");
+ return {store->requireStoreObjectAccessor(storePath)};
+ } else {
+ return SourcePath{getFSSourceAccessor(), CanonPath{path}}.resolveSymlinks();
+ }
+ }(),
+ Registry::Global);
}();
return reg;
diff --git a/src/libflake/flakeref.cc b/src/libflake/flakeref.cc
index a26f269c3..ed6b657ac 100644
--- a/src/libflake/flakeref.cc
+++ b/src/libflake/flakeref.cc
@@ -109,7 +109,8 @@ std::pair parsePathFlakeRefWithFragment(
std::smatch match;
auto succeeds = std::regex_match(url, match, pathFlakeRegex);
- assert(succeeds);
+ if (!succeeds)
+ throw Error("invalid flakeref '%s'", url);
auto path = match[1].str();
auto query = decodeQuery(match[3].str(), /*lenient=*/true);
auto fragment = percentDecode(match[5].str());
diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc
index 313a77563..024ed9785 100644
--- a/src/libstore-c/nix_api_store.cc
+++ b/src/libstore-c/nix_api_store.cc
@@ -7,6 +7,7 @@
#include "nix/store/store-api.hh"
#include "nix/store/store-open.hh"
#include "nix/store/build-result.hh"
+#include "nix/store/local-fs-store.hh"
#include "nix/store/globals.hh"
@@ -109,7 +110,8 @@ nix_err nix_store_real_path(
if (context)
context->last_err_code = NIX_OK;
try {
- auto res = store->ptr->toRealPath(path->path);
+ auto store2 = store->ptr.dynamic_pointer_cast();
+ auto res = store2 ? store2->toRealPath(path->path) : store->ptr->printStorePath(path->path);
return call_nix_get_string_callback(res, callback, user_data);
}
NIXC_CATCH_ERRS
diff --git a/src/libstore-tests/build-result.cc b/src/libstore-tests/build-result.cc
new file mode 100644
index 000000000..85e799c2a
--- /dev/null
+++ b/src/libstore-tests/build-result.cc
@@ -0,0 +1,108 @@
+#include
+
+#include "nix/store/build-result.hh"
+#include "nix/util/tests/json-characterization.hh"
+
+namespace nix {
+
+class BuildResultTest : public virtual CharacterizationTest
+{
+ std::filesystem::path unitTestData = getUnitTestData() / "build-result";
+
+public:
+ std::filesystem::path goldenMaster(std::string_view testStem) const override
+ {
+ return unitTestData / testStem;
+ }
+};
+
+using nlohmann::json;
+
+struct BuildResultJsonTest : BuildResultTest,
+ JsonCharacterizationTest,
+ ::testing::WithParamInterface>
+{};
+
+TEST_P(BuildResultJsonTest, from_json)
+{
+ auto & [name, expected] = GetParam();
+ readJsonTest(name, expected);
+}
+
+TEST_P(BuildResultJsonTest, to_json)
+{
+ auto & [name, value] = GetParam();
+ writeJsonTest(name, value);
+}
+
+using namespace std::literals::chrono_literals;
+
+INSTANTIATE_TEST_SUITE_P(
+ BuildResultJSON,
+ BuildResultJsonTest,
+ ::testing::Values(
+ std::pair{
+ "not-deterministic",
+ BuildResult{
+ .inner{BuildResult::Failure{
+ .status = BuildResult::Failure::NotDeterministic,
+ .errorMsg = "no idea why",
+ .isNonDeterministic = false, // Note: This field is separate from the status
+ }},
+ .timesBuilt = 1,
+ },
+ },
+ std::pair{
+ "output-rejected",
+ BuildResult{
+ .inner{BuildResult::Failure{
+ .status = BuildResult::Failure::OutputRejected,
+ .errorMsg = "no idea why",
+ .isNonDeterministic = false,
+ }},
+ .timesBuilt = 3,
+ .startTime = 30,
+ .stopTime = 50,
+ },
+ },
+ std::pair{
+ "success",
+ BuildResult{
+ .inner{BuildResult::Success{
+ .status = BuildResult::Success::Built,
+ .builtOutputs{
+ {
+ "foo",
+ {
+ {
+ .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
+ },
+ DrvOutput{
+ .drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
+ .outputName = "foo",
+ },
+ },
+ },
+ {
+ "bar",
+ {
+ {
+ .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
+ },
+ DrvOutput{
+ .drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
+ .outputName = "bar",
+ },
+ },
+ },
+ },
+ }},
+ .timesBuilt = 3,
+ .startTime = 30,
+ .stopTime = 50,
+ .cpuUser = std::chrono::microseconds(500s),
+ .cpuSystem = std::chrono::microseconds(604s),
+ },
+ }));
+
+} // namespace nix
diff --git a/src/libstore-tests/common-protocol.cc b/src/libstore-tests/common-protocol.cc
index 2c001957b..7c40e8cdb 100644
--- a/src/libstore-tests/common-protocol.cc
+++ b/src/libstore-tests/common-protocol.cc
@@ -114,13 +114,28 @@ CHARACTERIZATION_TEST(
Realisation{
{
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
- .signatures = {"asdf", "qwer"},
},
- DrvOutput{
+ {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
},
+ Realisation{
+ {
+ .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
+ .signatures = {"asdf", "qwer"},
+ },
+ {
+ .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
+ .outputName = "baz",
+ },
+ },
+ }))
+
+CHARACTERIZATION_TEST(
+ realisation_with_deps,
+ "realisation-with-deps",
+ (std::tuple{
Realisation{
{
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
diff --git a/src/libstore-tests/data/build-result/not-deterministic.json b/src/libstore-tests/data/build-result/not-deterministic.json
new file mode 100644
index 000000000..c24a15795
--- /dev/null
+++ b/src/libstore-tests/data/build-result/not-deterministic.json
@@ -0,0 +1,9 @@
+{
+ "errorMsg": "no idea why",
+ "isNonDeterministic": false,
+ "startTime": 0,
+ "status": "NotDeterministic",
+ "stopTime": 0,
+ "success": false,
+ "timesBuilt": 1
+}
diff --git a/src/libstore-tests/data/build-result/output-rejected.json b/src/libstore-tests/data/build-result/output-rejected.json
new file mode 100644
index 000000000..9494bf4ec
--- /dev/null
+++ b/src/libstore-tests/data/build-result/output-rejected.json
@@ -0,0 +1,9 @@
+{
+ "errorMsg": "no idea why",
+ "isNonDeterministic": false,
+ "startTime": 30,
+ "status": "OutputRejected",
+ "stopTime": 50,
+ "success": false,
+ "timesBuilt": 3
+}
diff --git a/src/libstore-tests/data/build-result/success.json b/src/libstore-tests/data/build-result/success.json
new file mode 100644
index 000000000..4baadb547
--- /dev/null
+++ b/src/libstore-tests/data/build-result/success.json
@@ -0,0 +1,23 @@
+{
+ "builtOutputs": {
+ "bar": {
+ "dependentRealisations": {},
+ "id": "sha256:6f869f9ea2823bda165e06076fd0de4366dead2c0e8d2dbbad277d4f15c373f5!bar",
+ "outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
+ "signatures": []
+ },
+ "foo": {
+ "dependentRealisations": {},
+ "id": "sha256:6f869f9ea2823bda165e06076fd0de4366dead2c0e8d2dbbad277d4f15c373f5!foo",
+ "outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
+ "signatures": []
+ }
+ },
+ "cpuSystem": 604000000,
+ "cpuUser": 500000000,
+ "startTime": 30,
+ "status": "Built",
+ "stopTime": 50,
+ "success": true,
+ "timesBuilt": 3
+}
diff --git a/src/libstore-tests/data/common-protocol/realisation-with-deps.bin b/src/libstore-tests/data/common-protocol/realisation-with-deps.bin
new file mode 100644
index 000000000..54a78b64e
Binary files /dev/null and b/src/libstore-tests/data/common-protocol/realisation-with-deps.bin differ
diff --git a/src/libstore-tests/data/common-protocol/realisation.bin b/src/libstore-tests/data/common-protocol/realisation.bin
index 2176c6c4a..3a0b2b2d8 100644
Binary files a/src/libstore-tests/data/common-protocol/realisation.bin and b/src/libstore-tests/data/common-protocol/realisation.bin differ
diff --git a/src/libstore-tests/data/derivation/ca/advanced-attributes-defaults.json b/src/libstore-tests/data/derivation/ca/advanced-attributes-defaults.json
index eb4bd4f3d..781b4cb14 100644
--- a/src/libstore-tests/data/derivation/ca/advanced-attributes-defaults.json
+++ b/src/libstore-tests/data/derivation/ca/advanced-attributes-defaults.json
@@ -12,8 +12,10 @@
"outputHashMode": "recursive",
"system": "my-system"
},
- "inputDrvs": {},
- "inputSrcs": [],
+ "inputs": {
+ "drvs": {},
+ "srcs": []
+ },
"name": "advanced-attributes-defaults",
"outputs": {
"out": {
@@ -22,5 +24,5 @@
}
},
"system": "my-system",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ca/advanced-attributes-structured-attrs-defaults.json b/src/libstore-tests/data/derivation/ca/advanced-attributes-structured-attrs-defaults.json
index 3a4a3079b..7437b51ef 100644
--- a/src/libstore-tests/data/derivation/ca/advanced-attributes-structured-attrs-defaults.json
+++ b/src/libstore-tests/data/derivation/ca/advanced-attributes-structured-attrs-defaults.json
@@ -8,8 +8,10 @@
"dev": "/02qcpld1y6xhs5gz9bchpxaw0xdhmsp5dv88lh25r2ss44kh8dxz",
"out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"
},
- "inputDrvs": {},
- "inputSrcs": [],
+ "inputs": {
+ "drvs": {},
+ "srcs": []
+ },
"name": "advanced-attributes-structured-attrs-defaults",
"outputs": {
"dev": {
@@ -33,5 +35,5 @@
"system": "my-system"
},
"system": "my-system",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ca/advanced-attributes-structured-attrs.json b/src/libstore-tests/data/derivation/ca/advanced-attributes-structured-attrs.json
index b10355af7..2a4e70558 100644
--- a/src/libstore-tests/data/derivation/ca/advanced-attributes-structured-attrs.json
+++ b/src/libstore-tests/data/derivation/ca/advanced-attributes-structured-attrs.json
@@ -9,25 +9,27 @@
"dev": "/02qcpld1y6xhs5gz9bchpxaw0xdhmsp5dv88lh25r2ss44kh8dxz",
"out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"
},
- "inputDrvs": {
- "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "dev",
- "out"
- ]
+ "inputs": {
+ "drvs": {
+ "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "dev",
+ "out"
+ ]
+ },
+ "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "dev",
+ "out"
+ ]
+ }
},
- "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "dev",
- "out"
- ]
- }
+ "srcs": [
+ "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
+ ]
},
- "inputSrcs": [
- "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
- ],
"name": "advanced-attributes-structured-attrs",
"outputs": {
"bin": {
@@ -101,5 +103,5 @@
"system": "my-system"
},
"system": "my-system",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ca/advanced-attributes.json b/src/libstore-tests/data/derivation/ca/advanced-attributes.json
index d66882036..55dbe62e0 100644
--- a/src/libstore-tests/data/derivation/ca/advanced-attributes.json
+++ b/src/libstore-tests/data/derivation/ca/advanced-attributes.json
@@ -25,25 +25,27 @@
"requiredSystemFeatures": "rainbow uid-range",
"system": "my-system"
},
- "inputDrvs": {
- "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "dev",
- "out"
- ]
+ "inputs": {
+ "drvs": {
+ "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "dev",
+ "out"
+ ]
+ },
+ "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "dev",
+ "out"
+ ]
+ }
},
- "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "dev",
- "out"
- ]
- }
+ "srcs": [
+ "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
+ ]
},
- "inputSrcs": [
- "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
- ],
"name": "advanced-attributes",
"outputs": {
"out": {
@@ -52,5 +54,5 @@
}
},
"system": "my-system",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ca/all_set.json b/src/libstore-tests/data/derivation/ca/all_set.json
new file mode 100644
index 000000000..e06eada01
--- /dev/null
+++ b/src/libstore-tests/data/derivation/ca/all_set.json
@@ -0,0 +1,46 @@
+{
+ "additionalSandboxProfile": "sandcastle",
+ "allowLocalNetworking": true,
+ "allowSubstitutes": false,
+ "exportReferencesGraph": {
+ "refs1": [
+ "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
+ ],
+ "refs2": [
+ "/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
+ ]
+ },
+ "impureEnvVars": [
+ "UNICORN"
+ ],
+ "impureHostDeps": [
+ "/usr/bin/ditto"
+ ],
+ "noChroot": true,
+ "outputChecks": {
+ "forAllOutputs": {
+ "allowedReferences": [
+ "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
+ ],
+ "allowedRequisites": [
+ "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"
+ ],
+ "disallowedReferences": [
+ "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"
+ ],
+ "disallowedRequisites": [
+ "/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"
+ ],
+ "ignoreSelfRefs": true,
+ "maxClosureSize": null,
+ "maxSize": null
+ }
+ },
+ "passAsFile": [],
+ "preferLocalBuild": true,
+ "requiredSystemFeatures": [
+ "rainbow",
+ "uid-range"
+ ],
+ "unsafeDiscardReferences": {}
+}
diff --git a/src/libstore-tests/data/derivation/ca/self-contained.json b/src/libstore-tests/data/derivation/ca/self-contained.json
index 331beb7be..c05710140 100644
--- a/src/libstore-tests/data/derivation/ca/self-contained.json
+++ b/src/libstore-tests/data/derivation/ca/self-contained.json
@@ -10,8 +10,10 @@
"out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9",
"system": "x86_64-linux"
},
- "inputDrvs": {},
- "inputSrcs": [],
+ "inputs": {
+ "drvs": {},
+ "srcs": []
+ },
"name": "myname",
"outputs": {
"out": {
@@ -20,5 +22,5 @@
}
},
"system": "x86_64-linux",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ca/structuredAttrs_all_set.json b/src/libstore-tests/data/derivation/ca/structuredAttrs_all_set.json
new file mode 100644
index 000000000..2a321897c
--- /dev/null
+++ b/src/libstore-tests/data/derivation/ca/structuredAttrs_all_set.json
@@ -0,0 +1,66 @@
+{
+ "additionalSandboxProfile": "sandcastle",
+ "allowLocalNetworking": true,
+ "allowSubstitutes": false,
+ "exportReferencesGraph": {
+ "refs1": [
+ "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
+ ],
+ "refs2": [
+ "/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
+ ]
+ },
+ "impureEnvVars": [
+ "UNICORN"
+ ],
+ "impureHostDeps": [
+ "/usr/bin/ditto"
+ ],
+ "noChroot": true,
+ "outputChecks": {
+ "perOutput": {
+ "bin": {
+ "allowedReferences": null,
+ "allowedRequisites": null,
+ "disallowedReferences": [
+ "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"
+ ],
+ "disallowedRequisites": [
+ "/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"
+ ],
+ "ignoreSelfRefs": false,
+ "maxClosureSize": null,
+ "maxSize": null
+ },
+ "dev": {
+ "allowedReferences": null,
+ "allowedRequisites": null,
+ "disallowedReferences": [],
+ "disallowedRequisites": [],
+ "ignoreSelfRefs": false,
+ "maxClosureSize": 5909,
+ "maxSize": 789
+ },
+ "out": {
+ "allowedReferences": [
+ "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
+ ],
+ "allowedRequisites": [
+ "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"
+ ],
+ "disallowedReferences": [],
+ "disallowedRequisites": [],
+ "ignoreSelfRefs": false,
+ "maxClosureSize": null,
+ "maxSize": null
+ }
+ }
+ },
+ "passAsFile": [],
+ "preferLocalBuild": true,
+ "requiredSystemFeatures": [
+ "rainbow",
+ "uid-range"
+ ],
+ "unsafeDiscardReferences": {}
+}
diff --git a/src/libstore-tests/data/derivation/dyn-dep-derivation.json b/src/libstore-tests/data/derivation/dyn-dep-derivation.json
index 1a9f54c53..1793c5f2d 100644
--- a/src/libstore-tests/data/derivation/dyn-dep-derivation.json
+++ b/src/libstore-tests/data/derivation/dyn-dep-derivation.json
@@ -7,33 +7,35 @@
"env": {
"BIG_BAD": "WOLF"
},
- "inputDrvs": {
- "c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": {
- "dynamicOutputs": {
- "cat": {
- "dynamicOutputs": {},
- "outputs": [
- "kitten"
- ]
+ "inputs": {
+ "drvs": {
+ "c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": {
+ "dynamicOutputs": {
+ "cat": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "kitten"
+ ]
+ },
+ "goose": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "gosling"
+ ]
+ }
},
- "goose": {
- "dynamicOutputs": {},
- "outputs": [
- "gosling"
- ]
- }
- },
- "outputs": [
- "cat",
- "dog"
- ]
- }
+ "outputs": [
+ "cat",
+ "dog"
+ ]
+ }
+ },
+ "srcs": [
+ "c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
+ ]
},
- "inputSrcs": [
- "c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
- ],
"name": "dyn-dep-derivation",
"outputs": {},
"system": "wasm-sel4",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ia/advanced-attributes-defaults.json b/src/libstore-tests/data/derivation/ia/advanced-attributes-defaults.json
index 0fa543f21..898762123 100644
--- a/src/libstore-tests/data/derivation/ia/advanced-attributes-defaults.json
+++ b/src/libstore-tests/data/derivation/ia/advanced-attributes-defaults.json
@@ -10,8 +10,10 @@
"out": "/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults",
"system": "my-system"
},
- "inputDrvs": {},
- "inputSrcs": [],
+ "inputs": {
+ "drvs": {},
+ "srcs": []
+ },
"name": "advanced-attributes-defaults",
"outputs": {
"out": {
@@ -19,5 +21,5 @@
}
},
"system": "my-system",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ia/advanced-attributes-structured-attrs-defaults.json b/src/libstore-tests/data/derivation/ia/advanced-attributes-structured-attrs-defaults.json
index e02392ea1..c51095986 100644
--- a/src/libstore-tests/data/derivation/ia/advanced-attributes-structured-attrs-defaults.json
+++ b/src/libstore-tests/data/derivation/ia/advanced-attributes-structured-attrs-defaults.json
@@ -8,8 +8,10 @@
"dev": "/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev",
"out": "/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults"
},
- "inputDrvs": {},
- "inputSrcs": [],
+ "inputs": {
+ "drvs": {},
+ "srcs": []
+ },
"name": "advanced-attributes-structured-attrs-defaults",
"outputs": {
"dev": {
@@ -29,5 +31,5 @@
"system": "my-system"
},
"system": "my-system",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ia/advanced-attributes-structured-attrs.json b/src/libstore-tests/data/derivation/ia/advanced-attributes-structured-attrs.json
index 9230b06b6..e07d1294b 100644
--- a/src/libstore-tests/data/derivation/ia/advanced-attributes-structured-attrs.json
+++ b/src/libstore-tests/data/derivation/ia/advanced-attributes-structured-attrs.json
@@ -9,25 +9,27 @@
"dev": "/nix/store/wyfgwsdi8rs851wmy1xfzdxy7y5vrg5l-advanced-attributes-structured-attrs-dev",
"out": "/nix/store/7cxy4zx1vqc885r4jl2l64pymqbdmhii-advanced-attributes-structured-attrs"
},
- "inputDrvs": {
- "afc3vbjbzql750v2lp8gxgaxsajphzih-foo.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "dev",
- "out"
- ]
+ "inputs": {
+ "drvs": {
+ "afc3vbjbzql750v2lp8gxgaxsajphzih-foo.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "dev",
+ "out"
+ ]
+ },
+ "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "dev",
+ "out"
+ ]
+ }
},
- "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "dev",
- "out"
- ]
- }
+ "srcs": [
+ "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
+ ]
},
- "inputSrcs": [
- "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
- ],
"name": "advanced-attributes-structured-attrs",
"outputs": {
"bin": {
@@ -96,5 +98,5 @@
"system": "my-system"
},
"system": "my-system",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ia/advanced-attributes.json b/src/libstore-tests/data/derivation/ia/advanced-attributes.json
index ba5911c91..372b4fbb9 100644
--- a/src/libstore-tests/data/derivation/ia/advanced-attributes.json
+++ b/src/libstore-tests/data/derivation/ia/advanced-attributes.json
@@ -23,25 +23,27 @@
"requiredSystemFeatures": "rainbow uid-range",
"system": "my-system"
},
- "inputDrvs": {
- "afc3vbjbzql750v2lp8gxgaxsajphzih-foo.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "dev",
- "out"
- ]
+ "inputs": {
+ "drvs": {
+ "afc3vbjbzql750v2lp8gxgaxsajphzih-foo.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "dev",
+ "out"
+ ]
+ },
+ "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "dev",
+ "out"
+ ]
+ }
},
- "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "dev",
- "out"
- ]
- }
+ "srcs": [
+ "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
+ ]
},
- "inputSrcs": [
- "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
- ],
"name": "advanced-attributes",
"outputs": {
"out": {
@@ -49,5 +51,5 @@
}
},
"system": "my-system",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/derivation/ia/all_set.json b/src/libstore-tests/data/derivation/ia/all_set.json
new file mode 100644
index 000000000..62b6cdf97
--- /dev/null
+++ b/src/libstore-tests/data/derivation/ia/all_set.json
@@ -0,0 +1,46 @@
+{
+ "additionalSandboxProfile": "sandcastle",
+ "allowLocalNetworking": true,
+ "allowSubstitutes": false,
+ "exportReferencesGraph": {
+ "refs1": [
+ "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
+ ],
+ "refs2": [
+ "/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
+ ]
+ },
+ "impureEnvVars": [
+ "UNICORN"
+ ],
+ "impureHostDeps": [
+ "/usr/bin/ditto"
+ ],
+ "noChroot": true,
+ "outputChecks": {
+ "forAllOutputs": {
+ "allowedReferences": [
+ "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
+ ],
+ "allowedRequisites": [
+ "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
+ ],
+ "disallowedReferences": [
+ "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"
+ ],
+ "disallowedRequisites": [
+ "/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"
+ ],
+ "ignoreSelfRefs": true,
+ "maxClosureSize": null,
+ "maxSize": null
+ }
+ },
+ "passAsFile": [],
+ "preferLocalBuild": true,
+ "requiredSystemFeatures": [
+ "rainbow",
+ "uid-range"
+ ],
+ "unsafeDiscardReferences": {}
+}
diff --git a/src/libstore-tests/data/derivation/ia/defaults.json b/src/libstore-tests/data/derivation/ia/defaults.json
new file mode 100644
index 000000000..79a68989c
--- /dev/null
+++ b/src/libstore-tests/data/derivation/ia/defaults.json
@@ -0,0 +1,24 @@
+{
+ "additionalSandboxProfile": "",
+ "allowLocalNetworking": false,
+ "allowSubstitutes": true,
+ "exportReferencesGraph": {},
+ "impureEnvVars": [],
+ "impureHostDeps": [],
+ "noChroot": false,
+ "outputChecks": {
+ "forAllOutputs": {
+ "allowedReferences": null,
+ "allowedRequisites": null,
+ "disallowedReferences": [],
+ "disallowedRequisites": [],
+ "ignoreSelfRefs": true,
+ "maxClosureSize": null,
+ "maxSize": null
+ }
+ },
+ "passAsFile": [],
+ "preferLocalBuild": false,
+ "requiredSystemFeatures": [],
+ "unsafeDiscardReferences": {}
+}
diff --git a/src/libstore-tests/data/derivation/ia/structuredAttrs_all_set.json b/src/libstore-tests/data/derivation/ia/structuredAttrs_all_set.json
new file mode 100644
index 000000000..0fa383589
--- /dev/null
+++ b/src/libstore-tests/data/derivation/ia/structuredAttrs_all_set.json
@@ -0,0 +1,66 @@
+{
+ "additionalSandboxProfile": "sandcastle",
+ "allowLocalNetworking": true,
+ "allowSubstitutes": false,
+ "exportReferencesGraph": {
+ "refs1": [
+ "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
+ ],
+ "refs2": [
+ "/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
+ ]
+ },
+ "impureEnvVars": [
+ "UNICORN"
+ ],
+ "impureHostDeps": [
+ "/usr/bin/ditto"
+ ],
+ "noChroot": true,
+ "outputChecks": {
+ "perOutput": {
+ "bin": {
+ "allowedReferences": null,
+ "allowedRequisites": null,
+ "disallowedReferences": [
+ "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"
+ ],
+ "disallowedRequisites": [
+ "/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"
+ ],
+ "ignoreSelfRefs": false,
+ "maxClosureSize": null,
+ "maxSize": null
+ },
+ "dev": {
+ "allowedReferences": null,
+ "allowedRequisites": null,
+ "disallowedReferences": [],
+ "disallowedRequisites": [],
+ "ignoreSelfRefs": false,
+ "maxClosureSize": 5909,
+ "maxSize": 789
+ },
+ "out": {
+ "allowedReferences": [
+ "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
+ ],
+ "allowedRequisites": [
+ "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
+ ],
+ "disallowedReferences": [],
+ "disallowedRequisites": [],
+ "ignoreSelfRefs": false,
+ "maxClosureSize": null,
+ "maxSize": null
+ }
+ }
+ },
+ "passAsFile": [],
+ "preferLocalBuild": true,
+ "requiredSystemFeatures": [
+ "rainbow",
+ "uid-range"
+ ],
+ "unsafeDiscardReferences": {}
+}
diff --git a/src/libstore-tests/data/derivation/ia/structuredAttrs_defaults.json b/src/libstore-tests/data/derivation/ia/structuredAttrs_defaults.json
new file mode 100644
index 000000000..d898d85f5
--- /dev/null
+++ b/src/libstore-tests/data/derivation/ia/structuredAttrs_defaults.json
@@ -0,0 +1,16 @@
+{
+ "additionalSandboxProfile": "",
+ "allowLocalNetworking": false,
+ "allowSubstitutes": true,
+ "exportReferencesGraph": {},
+ "impureEnvVars": [],
+ "impureHostDeps": [],
+ "noChroot": false,
+ "outputChecks": {
+ "perOutput": {}
+ },
+ "passAsFile": [],
+ "preferLocalBuild": false,
+ "requiredSystemFeatures": [],
+ "unsafeDiscardReferences": {}
+}
diff --git a/src/libstore-tests/data/derivation/output-caFixedFlat.json b/src/libstore-tests/data/derivation/output-caFixedFlat.json
index e6a0123f6..9a38608b3 100644
--- a/src/libstore-tests/data/derivation/output-caFixedFlat.json
+++ b/src/libstore-tests/data/derivation/output-caFixedFlat.json
@@ -1,5 +1,8 @@
{
- "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
- "hashAlgo": "sha256",
+ "hash": {
+ "algorithm": "sha256",
+ "format": "base64",
+ "hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
+ },
"method": "flat"
}
diff --git a/src/libstore-tests/data/derivation/output-caFixedNAR.json b/src/libstore-tests/data/derivation/output-caFixedNAR.json
index b57e065a9..767c605a3 100644
--- a/src/libstore-tests/data/derivation/output-caFixedNAR.json
+++ b/src/libstore-tests/data/derivation/output-caFixedNAR.json
@@ -1,5 +1,8 @@
{
- "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
- "hashAlgo": "sha256",
+ "hash": {
+ "algorithm": "sha256",
+ "format": "base64",
+ "hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
+ },
"method": "nar"
}
diff --git a/src/libstore-tests/data/derivation/output-caFixedText.json b/src/libstore-tests/data/derivation/output-caFixedText.json
index 84778509e..a04f1ff2a 100644
--- a/src/libstore-tests/data/derivation/output-caFixedText.json
+++ b/src/libstore-tests/data/derivation/output-caFixedText.json
@@ -1,5 +1,8 @@
{
- "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
- "hashAlgo": "sha256",
+ "hash": {
+ "algorithm": "sha256",
+ "format": "base64",
+ "hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
+ },
"method": "text"
}
diff --git a/src/libstore-tests/data/derivation/simple-derivation.json b/src/libstore-tests/data/derivation/simple-derivation.json
index 41a049aef..04129a096 100644
--- a/src/libstore-tests/data/derivation/simple-derivation.json
+++ b/src/libstore-tests/data/derivation/simple-derivation.json
@@ -7,20 +7,22 @@
"env": {
"BIG_BAD": "WOLF"
},
- "inputDrvs": {
- "c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": {
- "dynamicOutputs": {},
- "outputs": [
- "cat",
- "dog"
- ]
- }
+ "inputs": {
+ "drvs": {
+ "c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "cat",
+ "dog"
+ ]
+ }
+ },
+ "srcs": [
+ "c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
+ ]
},
- "inputSrcs": [
- "c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
- ],
"name": "simple-derivation",
"outputs": {},
"system": "wasm-sel4",
- "version": 3
+ "version": 4
}
diff --git a/src/libstore-tests/data/nar-info/impure.json b/src/libstore-tests/data/nar-info/impure.json
index bb9791a6a..f35ff990b 100644
--- a/src/libstore-tests/data/nar-info/impure.json
+++ b/src/libstore-tests/data/nar-info/impure.json
@@ -1,5 +1,12 @@
{
- "ca": "fixed:r:sha256:1lr187v6dck1rjh2j6svpikcfz53wyl3qrlcbb405zlh13x0khhh",
+ "ca": {
+ "hash": {
+ "algorithm": "sha256",
+ "format": "base64",
+ "hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
+ },
+ "method": "nar"
+ },
"compression": "xz",
"deriver": "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"downloadHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
@@ -16,5 +23,6 @@
"qwer"
],
"ultimate": true,
- "url": "nar/1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3.nar.xz"
+ "url": "nar/1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3.nar.xz",
+ "version": 2
}
diff --git a/src/libstore-tests/data/nar-info/pure.json b/src/libstore-tests/data/nar-info/pure.json
index 955baec31..2c5cb3bde 100644
--- a/src/libstore-tests/data/nar-info/pure.json
+++ b/src/libstore-tests/data/nar-info/pure.json
@@ -1,9 +1,17 @@
{
- "ca": "fixed:r:sha256:1lr187v6dck1rjh2j6svpikcfz53wyl3qrlcbb405zlh13x0khhh",
+ "ca": {
+ "hash": {
+ "algorithm": "sha256",
+ "format": "base64",
+ "hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
+ },
+ "method": "nar"
+ },
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"/nix/store/n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
- ]
+ ],
+ "version": 2
}
diff --git a/src/libstore-tests/data/path-info/empty_impure.json b/src/libstore-tests/data/path-info/empty_impure.json
index be982dcef..381acaa03 100644
--- a/src/libstore-tests/data/path-info/empty_impure.json
+++ b/src/libstore-tests/data/path-info/empty_impure.json
@@ -6,5 +6,6 @@
"references": [],
"registrationTime": null,
"signatures": [],
- "ultimate": false
+ "ultimate": false,
+ "version": 2
}
diff --git a/src/libstore-tests/data/path-info/empty_pure.json b/src/libstore-tests/data/path-info/empty_pure.json
index 10d9f508a..6d3fa646b 100644
--- a/src/libstore-tests/data/path-info/empty_pure.json
+++ b/src/libstore-tests/data/path-info/empty_pure.json
@@ -2,5 +2,6 @@
"ca": null,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 0,
- "references": []
+ "references": [],
+ "version": 2
}
diff --git a/src/libstore-tests/data/path-info/impure.json b/src/libstore-tests/data/path-info/impure.json
index 0c452cc49..141b38a16 100644
--- a/src/libstore-tests/data/path-info/impure.json
+++ b/src/libstore-tests/data/path-info/impure.json
@@ -1,5 +1,12 @@
{
- "ca": "fixed:r:sha256:1lr187v6dck1rjh2j6svpikcfz53wyl3qrlcbb405zlh13x0khhh",
+ "ca": {
+ "hash": {
+ "algorithm": "sha256",
+ "format": "base64",
+ "hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
+ },
+ "method": "nar"
+ },
"deriver": "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
@@ -12,5 +19,6 @@
"asdf",
"qwer"
],
- "ultimate": true
+ "ultimate": true,
+ "version": 2
}
diff --git a/src/libstore-tests/data/path-info/pure.json b/src/libstore-tests/data/path-info/pure.json
index 955baec31..2c5cb3bde 100644
--- a/src/libstore-tests/data/path-info/pure.json
+++ b/src/libstore-tests/data/path-info/pure.json
@@ -1,9 +1,17 @@
{
- "ca": "fixed:r:sha256:1lr187v6dck1rjh2j6svpikcfz53wyl3qrlcbb405zlh13x0khhh",
+ "ca": {
+ "hash": {
+ "algorithm": "sha256",
+ "format": "base64",
+ "hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
+ },
+ "method": "nar"
+ },
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"/nix/store/n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
- ]
+ ],
+ "version": 2
}
diff --git a/src/libstore-tests/data/serve-protocol/realisation-with-deps.bin b/src/libstore-tests/data/serve-protocol/realisation-with-deps.bin
new file mode 100644
index 000000000..54a78b64e
Binary files /dev/null and b/src/libstore-tests/data/serve-protocol/realisation-with-deps.bin differ
diff --git a/src/libstore-tests/data/serve-protocol/realisation.bin b/src/libstore-tests/data/serve-protocol/realisation.bin
index 2176c6c4a..3a0b2b2d8 100644
Binary files a/src/libstore-tests/data/serve-protocol/realisation.bin and b/src/libstore-tests/data/serve-protocol/realisation.bin differ
diff --git a/src/libstore-tests/data/worker-protocol/realisation-with-deps.bin b/src/libstore-tests/data/worker-protocol/realisation-with-deps.bin
new file mode 100644
index 000000000..54a78b64e
Binary files /dev/null and b/src/libstore-tests/data/worker-protocol/realisation-with-deps.bin differ
diff --git a/src/libstore-tests/data/worker-protocol/realisation.bin b/src/libstore-tests/data/worker-protocol/realisation.bin
index 2176c6c4a..3a0b2b2d8 100644
Binary files a/src/libstore-tests/data/worker-protocol/realisation.bin and b/src/libstore-tests/data/worker-protocol/realisation.bin differ
diff --git a/src/libstore-tests/derivation-advanced-attrs.cc b/src/libstore-tests/derivation-advanced-attrs.cc
index 02bc8fa24..41538cdcc 100644
--- a/src/libstore-tests/derivation-advanced-attrs.cc
+++ b/src/libstore-tests/derivation-advanced-attrs.cc
@@ -10,13 +10,15 @@
#include "nix/util/json-utils.hh"
#include "nix/store/tests/libstore.hh"
-#include "nix/util/tests/characterization.hh"
+#include "nix/util/tests/json-characterization.hh"
namespace nix {
using namespace nlohmann;
-class DerivationAdvancedAttrsTest : public CharacterizationTest, public LibStoreTest
+class DerivationAdvancedAttrsTest : public JsonCharacterizationTest,
+ public JsonCharacterizationTest,
+ public LibStoreTest
{
protected:
std::filesystem::path unitTestData = getUnitTestData() / "derivation" / "ia";
@@ -32,6 +34,33 @@ public:
* to worry about race conditions if the tests run concurrently.
*/
ExperimentalFeatureSettings mockXpSettings;
+
+ /**
+ * Helper function to test getRequiredSystemFeatures for a given derivation file
+ */
+ void testRequiredSystemFeatures(const std::string & fileName, const StringSet & expectedFeatures)
+ {
+ this->readTest(fileName, [&](auto encoded) {
+ auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
+ DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
+ EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedFeatures);
+ });
+ }
+
+ /**
+ * Helper function to test DerivationOptions parsing and comparison
+ */
+ void testDerivationOptions(
+ const std::string & fileName, const DerivationOptions & expected, const StringSet & expectedSystemFeatures)
+ {
+ this->readTest(fileName, [&](auto encoded) {
+ auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
+ DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
+
+ EXPECT_EQ(options, expected);
+ EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedSystemFeatures);
+ });
+ }
};
class CaDerivationAdvancedAttrsTest : public DerivationAdvancedAttrsTest
@@ -100,33 +129,35 @@ TEST_ATERM_JSON(advancedAttributes_structuredAttrs_defaults, "advanced-attribute
using ExportReferencesMap = decltype(DerivationOptions::exportReferencesGraph);
+static const DerivationOptions advancedAttributes_defaults = {
+ .outputChecks =
+ DerivationOptions::OutputChecks{
+ .ignoreSelfRefs = true,
+ },
+ .unsafeDiscardReferences = {},
+ .passAsFile = {},
+ .exportReferencesGraph = {},
+ .additionalSandboxProfile = "",
+ .noChroot = false,
+ .impureHostDeps = {},
+ .impureEnvVars = {},
+ .allowLocalNetworking = false,
+ .requiredSystemFeatures = {},
+ .preferLocalBuild = false,
+ .allowSubstitutes = true,
+};
+
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults)
{
this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_TRUE(!got.structuredAttrs);
- EXPECT_EQ(options.additionalSandboxProfile, "");
- EXPECT_EQ(options.noChroot, false);
- EXPECT_EQ(options.impureHostDeps, StringSet{});
- EXPECT_EQ(options.impureEnvVars, StringSet{});
- EXPECT_EQ(options.allowLocalNetworking, false);
- EXPECT_EQ(options.exportReferencesGraph, ExportReferencesMap{});
- {
- auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks);
- ASSERT_TRUE(checksForAllOutputs_ != nullptr);
- auto & checksForAllOutputs = *checksForAllOutputs_;
+ EXPECT_EQ(options, advancedAttributes_defaults);
- EXPECT_EQ(checksForAllOutputs.allowedReferences, std::nullopt);
- EXPECT_EQ(checksForAllOutputs.allowedRequisites, std::nullopt);
- EXPECT_EQ(checksForAllOutputs.disallowedReferences, StringSet{});
- EXPECT_EQ(checksForAllOutputs.disallowedRequisites, StringSet{});
- }
EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
EXPECT_EQ(options.substitutesAllowed(), true);
@@ -136,152 +167,124 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults)
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_defaults)
{
- this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) {
- auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
-
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
- DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
-
- EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{});
- });
+ testRequiredSystemFeatures("advanced-attributes-defaults.drv", {});
};
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_defaults)
{
- this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) {
- auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
-
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
- DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
-
- EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{"ca-derivations"});
- });
+ testRequiredSystemFeatures("advanced-attributes-defaults.drv", {"ca-derivations"});
};
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes)
{
+ DerivationOptions expected = {
+ .outputChecks =
+ DerivationOptions::OutputChecks{
+ .ignoreSelfRefs = true,
+ },
+ .unsafeDiscardReferences = {},
+ .passAsFile = {},
+ .additionalSandboxProfile = "sandcastle",
+ .noChroot = true,
+ .impureHostDeps = {"/usr/bin/ditto"},
+ .impureEnvVars = {"UNICORN"},
+ .allowLocalNetworking = true,
+ .requiredSystemFeatures = {"rainbow", "uid-range"},
+ .preferLocalBuild = true,
+ .allowSubstitutes = false,
+ };
+
this->readTest("advanced-attributes.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_TRUE(!got.structuredAttrs);
- EXPECT_EQ(options.additionalSandboxProfile, "sandcastle");
- EXPECT_EQ(options.noChroot, true);
- EXPECT_EQ(options.impureHostDeps, StringSet{"/usr/bin/ditto"});
- EXPECT_EQ(options.impureEnvVars, StringSet{"UNICORN"});
- EXPECT_EQ(options.allowLocalNetworking, true);
- EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
- EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
+ // Reset fields that vary between test cases to enable whole-object comparison
+ options.outputChecks = DerivationOptions::OutputChecks{.ignoreSelfRefs = true};
+ options.exportReferencesGraph = {};
+
+ EXPECT_EQ(options, expected);
+
EXPECT_EQ(options.substitutesAllowed(), false);
EXPECT_EQ(options.useUidRange(got), true);
});
};
-TEST_F(DerivationAdvancedAttrsTest, advancedAttributes)
+DerivationOptions advancedAttributes_ia = {
+ .outputChecks =
+ DerivationOptions::OutputChecks{
+ .ignoreSelfRefs = true,
+ .allowedReferences = StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"},
+ .disallowedReferences = StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"},
+ .allowedRequisites = StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"},
+ .disallowedRequisites = StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"},
+ },
+ .unsafeDiscardReferences = {},
+ .passAsFile = {},
+ .exportReferencesGraph{
+ {"refs1", {"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"}},
+ {"refs2", {"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"}},
+ },
+ .additionalSandboxProfile = "sandcastle",
+ .noChroot = true,
+ .impureHostDeps = {"/usr/bin/ditto"},
+ .impureEnvVars = {"UNICORN"},
+ .allowLocalNetworking = true,
+ .requiredSystemFeatures = {"rainbow", "uid-range"},
+ .preferLocalBuild = true,
+ .allowSubstitutes = false,
+};
+
+TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_ia)
{
- this->readTest("advanced-attributes.drv", [&](auto encoded) {
- auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
+ testDerivationOptions("advanced-attributes.drv", advancedAttributes_ia, {"rainbow", "uid-range"});
+};
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
- DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
-
- EXPECT_EQ(
- options.exportReferencesGraph,
- (ExportReferencesMap{
- {
- "refs1",
- {
- "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo",
- },
- },
- {
- "refs2",
- {
- "/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv",
- },
- },
- }));
-
- {
- auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks);
- ASSERT_TRUE(checksForAllOutputs_ != nullptr);
- auto & checksForAllOutputs = *checksForAllOutputs_;
-
- EXPECT_EQ(
- checksForAllOutputs.allowedReferences, StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"});
- EXPECT_EQ(
- checksForAllOutputs.allowedRequisites,
- StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"});
- EXPECT_EQ(
- checksForAllOutputs.disallowedReferences, StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"});
- EXPECT_EQ(
- checksForAllOutputs.disallowedRequisites,
- StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"});
- }
-
- StringSet systemFeatures{"rainbow", "uid-range"};
-
- EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
- });
+DerivationOptions advancedAttributes_ca = {
+ .outputChecks =
+ DerivationOptions::OutputChecks{
+ .ignoreSelfRefs = true,
+ .allowedReferences = StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"},
+ .disallowedReferences = StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"},
+ .allowedRequisites = StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"},
+ .disallowedRequisites = StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"},
+ },
+ .unsafeDiscardReferences = {},
+ .passAsFile = {},
+ .exportReferencesGraph{
+ {"refs1", {"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"}},
+ {"refs2", {"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}},
+ },
+ .additionalSandboxProfile = "sandcastle",
+ .noChroot = true,
+ .impureHostDeps = {"/usr/bin/ditto"},
+ .impureEnvVars = {"UNICORN"},
+ .allowLocalNetworking = true,
+ .requiredSystemFeatures = {"rainbow", "uid-range"},
+ .preferLocalBuild = true,
+ .allowSubstitutes = false,
};
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes)
{
- this->readTest("advanced-attributes.drv", [&](auto encoded) {
- auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
+ testDerivationOptions("advanced-attributes.drv", advancedAttributes_ca, {"rainbow", "uid-range", "ca-derivations"});
+};
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
- DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
-
- EXPECT_EQ(
- options.exportReferencesGraph,
- (ExportReferencesMap{
- {
- "refs1",
- {
- "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9",
- },
- },
- {
- "refs2",
- {
- "/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
- },
- },
- }));
-
- {
- auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks);
- ASSERT_TRUE(checksForAllOutputs_ != nullptr);
- auto & checksForAllOutputs = *checksForAllOutputs_;
-
- EXPECT_EQ(
- checksForAllOutputs.allowedReferences,
- StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"});
- EXPECT_EQ(
- checksForAllOutputs.allowedRequisites,
- StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"});
- EXPECT_EQ(
- checksForAllOutputs.disallowedReferences,
- StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"});
- EXPECT_EQ(
- checksForAllOutputs.disallowedRequisites,
- StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"});
- }
-
- StringSet systemFeatures{"rainbow", "uid-range"};
- systemFeatures.insert("ca-derivations");
-
- EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
- });
+DerivationOptions advancedAttributes_structuredAttrs_defaults = {
+ .outputChecks = std::map{},
+ .unsafeDiscardReferences = {},
+ .passAsFile = {},
+ .exportReferencesGraph = {},
+ .additionalSandboxProfile = "",
+ .noChroot = false,
+ .impureHostDeps = {},
+ .impureEnvVars = {},
+ .allowLocalNetworking = false,
+ .requiredSystemFeatures = {},
+ .preferLocalBuild = false,
+ .allowSubstitutes = true,
};
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_defaults)
@@ -289,26 +292,11 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_d
this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_TRUE(got.structuredAttrs);
- EXPECT_EQ(options.additionalSandboxProfile, "");
- EXPECT_EQ(options.noChroot, false);
- EXPECT_EQ(options.impureHostDeps, StringSet{});
- EXPECT_EQ(options.impureEnvVars, StringSet{});
- EXPECT_EQ(options.allowLocalNetworking, false);
- EXPECT_EQ(options.exportReferencesGraph, ExportReferencesMap{});
-
- {
- auto * checksPerOutput_ = std::get_if<1>(&options.outputChecks);
- ASSERT_TRUE(checksPerOutput_ != nullptr);
- auto & checksPerOutput = *checksPerOutput_;
-
- EXPECT_EQ(checksPerOutput.size(), 0u);
- }
+ EXPECT_EQ(options, advancedAttributes_structuredAttrs_defaults);
EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
@@ -319,55 +307,61 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_d
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults)
{
- this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) {
- auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
-
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
- DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
-
- EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{});
- });
+ testRequiredSystemFeatures("advanced-attributes-structured-attrs-defaults.drv", {});
};
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults)
{
- this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) {
- auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
-
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
- DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
-
- EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{"ca-derivations"});
- });
+ testRequiredSystemFeatures("advanced-attributes-structured-attrs-defaults.drv", {"ca-derivations"});
};
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
{
+ DerivationOptions expected = {
+ .outputChecks =
+ std::map{
+ {"dev",
+ DerivationOptions::OutputChecks{
+ .maxSize = 789,
+ .maxClosureSize = 5909,
+ }},
+ },
+ .unsafeDiscardReferences = {},
+ .passAsFile = {},
+ .exportReferencesGraph = {},
+ .additionalSandboxProfile = "sandcastle",
+ .noChroot = true,
+ .impureHostDeps = {"/usr/bin/ditto"},
+ .impureEnvVars = {"UNICORN"},
+ .allowLocalNetworking = true,
+ .requiredSystemFeatures = {"rainbow", "uid-range"},
+ .preferLocalBuild = true,
+ .allowSubstitutes = false,
+ };
+
this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_TRUE(got.structuredAttrs);
- EXPECT_EQ(options.additionalSandboxProfile, "sandcastle");
- EXPECT_EQ(options.noChroot, true);
- EXPECT_EQ(options.impureHostDeps, StringSet{"/usr/bin/ditto"});
- EXPECT_EQ(options.impureEnvVars, StringSet{"UNICORN"});
- EXPECT_EQ(options.allowLocalNetworking, true);
-
+ // Reset fields that vary between test cases to enable whole-object comparison
{
- auto output_ = get(std::get<1>(options.outputChecks), "dev");
- ASSERT_TRUE(output_);
- auto & output = *output_;
-
- EXPECT_EQ(output.maxSize, 789);
- EXPECT_EQ(output.maxClosureSize, 5909);
+ // Delete all keys but "dev" in options.outputChecks
+ auto * outputChecksMapP =
+ std::get_if>(&options.outputChecks);
+ ASSERT_TRUE(outputChecksMapP);
+ auto & outputChecksMap = *outputChecksMapP;
+ auto devEntry = outputChecksMap.find("dev");
+ ASSERT_TRUE(devEntry != outputChecksMap.end());
+ auto devChecks = devEntry->second;
+ outputChecksMap.clear();
+ outputChecksMap.emplace("dev", std::move(devChecks));
}
+ options.exportReferencesGraph = {};
+
+ EXPECT_EQ(options, expected);
EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
@@ -376,112 +370,109 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
});
};
+DerivationOptions advancedAttributes_structuredAttrs_ia = {
+ .outputChecks =
+ std::map{
+ {"out",
+ DerivationOptions::OutputChecks{
+ .allowedReferences = StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"},
+ .allowedRequisites = StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"},
+ }},
+ {"bin",
+ DerivationOptions::OutputChecks{
+ .disallowedReferences = StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"},
+ .disallowedRequisites = StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"},
+ }},
+ {"dev",
+ DerivationOptions::OutputChecks{
+ .maxSize = 789,
+ .maxClosureSize = 5909,
+ }},
+ },
+ .unsafeDiscardReferences = {},
+ .passAsFile = {},
+ .exportReferencesGraph =
+ {
+ {"refs1", {"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"}},
+ {"refs2", {"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"}},
+ },
+ .additionalSandboxProfile = "sandcastle",
+ .noChroot = true,
+ .impureHostDeps = {"/usr/bin/ditto"},
+ .impureEnvVars = {"UNICORN"},
+ .allowLocalNetworking = true,
+ .requiredSystemFeatures = {"rainbow", "uid-range"},
+ .preferLocalBuild = true,
+ .allowSubstitutes = false,
+};
+
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs)
{
- this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) {
- auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
-
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
- DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
-
- EXPECT_EQ(
- options.exportReferencesGraph,
- (ExportReferencesMap{
- {
- "refs1",
- {
- "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo",
- },
- },
- {
- "refs2",
- {
- "/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv",
- },
- },
- }));
+ testDerivationOptions(
+ "advanced-attributes-structured-attrs.drv", advancedAttributes_structuredAttrs_ia, {"rainbow", "uid-range"});
+};
+DerivationOptions advancedAttributes_structuredAttrs_ca = {
+ .outputChecks =
+ std::map{
+ {"out",
+ DerivationOptions::OutputChecks{
+ .allowedReferences = StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"},
+ .allowedRequisites = StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"},
+ }},
+ {"bin",
+ DerivationOptions::OutputChecks{
+ .disallowedReferences = StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"},
+ .disallowedRequisites = StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"},
+ }},
+ {"dev",
+ DerivationOptions::OutputChecks{
+ .maxSize = 789,
+ .maxClosureSize = 5909,
+ }},
+ },
+ .unsafeDiscardReferences = {},
+ .passAsFile = {},
+ .exportReferencesGraph =
{
- {
- auto output_ = get(std::get<1>(options.outputChecks), "out");
- ASSERT_TRUE(output_);
- auto & output = *output_;
-
- EXPECT_EQ(output.allowedReferences, StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"});
- EXPECT_EQ(output.allowedRequisites, StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"});
- }
-
- {
- auto output_ = get(std::get<1>(options.outputChecks), "bin");
- ASSERT_TRUE(output_);
- auto & output = *output_;
-
- EXPECT_EQ(output.disallowedReferences, StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"});
- EXPECT_EQ(
- output.disallowedRequisites, StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"});
- }
- }
-
- StringSet systemFeatures{"rainbow", "uid-range"};
-
- EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
- });
+ {"refs1", {"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"}},
+ {"refs2", {"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}},
+ },
+ .additionalSandboxProfile = "sandcastle",
+ .noChroot = true,
+ .impureHostDeps = {"/usr/bin/ditto"},
+ .impureEnvVars = {"UNICORN"},
+ .allowLocalNetworking = true,
+ .requiredSystemFeatures = {"rainbow", "uid-range"},
+ .preferLocalBuild = true,
+ .allowSubstitutes = false,
};
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs)
{
- this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) {
- auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
-
- auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
-
- DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
-
- EXPECT_EQ(
- options.exportReferencesGraph,
- (ExportReferencesMap{
- {
- "refs1",
- {
- "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9",
- },
- },
- {
- "refs2",
- {
- "/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
- },
- },
- }));
-
- {
- {
- auto output_ = get(std::get<1>(options.outputChecks), "out");
- ASSERT_TRUE(output_);
- auto & output = *output_;
-
- EXPECT_EQ(output.allowedReferences, StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"});
- EXPECT_EQ(output.allowedRequisites, StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"});
- }
-
- {
- auto output_ = get(std::get<1>(options.outputChecks), "bin");
- ASSERT_TRUE(output_);
- auto & output = *output_;
-
- EXPECT_EQ(
- output.disallowedReferences, StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"});
- EXPECT_EQ(
- output.disallowedRequisites, StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"});
- }
- }
-
- StringSet systemFeatures{"rainbow", "uid-range"};
- systemFeatures.insert("ca-derivations");
-
- EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
- });
+ testDerivationOptions(
+ "advanced-attributes-structured-attrs.drv",
+ advancedAttributes_structuredAttrs_ca,
+ {"rainbow", "uid-range", "ca-derivations"});
};
+#define TEST_JSON_OPTIONS(FIXUTURE, VAR, VAR2) \
+ TEST_F(FIXUTURE, DerivationOptions_##VAR##_from_json) \
+ { \
+ this->JsonCharacterizationTest::readJsonTest(#VAR, advancedAttributes_##VAR2); \
+ } \
+ TEST_F(FIXUTURE, DerivationOptions_##VAR##_to_json) \
+ { \
+ this->JsonCharacterizationTest::writeJsonTest(#VAR, advancedAttributes_##VAR2); \
+ }
+
+TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, defaults, defaults)
+TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, all_set, ia)
+TEST_JSON_OPTIONS(CaDerivationAdvancedAttrsTest, all_set, ca)
+TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, structuredAttrs_defaults, structuredAttrs_defaults)
+TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, structuredAttrs_all_set, structuredAttrs_ia)
+TEST_JSON_OPTIONS(CaDerivationAdvancedAttrsTest, structuredAttrs_all_set, structuredAttrs_ca)
+
+#undef TEST_JSON_OPTIONS
+
} // namespace nix
diff --git a/src/libstore-tests/meson.build b/src/libstore-tests/meson.build
index 4d464ad89..f76df8bcb 100644
--- a/src/libstore-tests/meson.build
+++ b/src/libstore-tests/meson.build
@@ -54,6 +54,7 @@ deps_private += gtest
subdir('nix-meson-build-support/common')
sources = files(
+ 'build-result.cc',
'common-protocol.cc',
'content-address.cc',
'derivation-advanced-attrs.cc',
diff --git a/src/libstore-tests/nix_api_store.cc b/src/libstore-tests/nix_api_store.cc
index 228b8069f..bf411053a 100644
--- a/src/libstore-tests/nix_api_store.cc
+++ b/src/libstore-tests/nix_api_store.cc
@@ -636,7 +636,7 @@ TEST_F(NixApiStoreTestWithRealisedPath, nix_store_realise_output_ordering)
auto outj_ph = nix::hashPlaceholder("outj");
std::string drvJson = R"({
- "version": 3,
+ "version": 4,
"name": "multi-output-test",
"system": ")" + nix::settings.thisSystem.get()
+ R"(",
@@ -668,8 +668,10 @@ TEST_F(NixApiStoreTestWithRealisedPath, nix_store_realise_output_ordering)
"outa": ")" + outa_ph
+ R"("
},
- "inputDrvs": {},
- "inputSrcs": [],
+ "inputs": {
+ "drvs": {},
+ "srcs": []
+ },
"outputs": {
"outd": { "hashAlgo": "sha256", "method": "nar" },
"outf": { "hashAlgo": "sha256", "method": "nar" },
diff --git a/src/libstore-tests/serve-protocol.cc b/src/libstore-tests/serve-protocol.cc
index 10aa21e9d..a7b69821c 100644
--- a/src/libstore-tests/serve-protocol.cc
+++ b/src/libstore-tests/serve-protocol.cc
@@ -94,6 +94,15 @@ VERSIONED_CHARACTERIZATION_TEST(
"realisation",
defaultVersion,
(std::tuple{
+ Realisation{
+ {
+ .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
+ },
+ {
+ .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
+ .outputName = "baz",
+ },
+ },
Realisation{
{
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
@@ -104,6 +113,14 @@ VERSIONED_CHARACTERIZATION_TEST(
.outputName = "baz",
},
},
+ }))
+
+VERSIONED_CHARACTERIZATION_TEST(
+ ServeProtoTest,
+ realisation_with_deps,
+ "realisation-with-deps",
+ defaultVersion,
+ (std::tuple{
Realisation{
{
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
diff --git a/src/libstore-tests/worker-protocol.cc b/src/libstore-tests/worker-protocol.cc
index c4afde3bd..8f70e937b 100644
--- a/src/libstore-tests/worker-protocol.cc
+++ b/src/libstore-tests/worker-protocol.cc
@@ -150,13 +150,30 @@ VERSIONED_CHARACTERIZATION_TEST(
Realisation{
{
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
- .signatures = {"asdf", "qwer"},
},
- DrvOutput{
+ {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
},
+ Realisation{
+ {
+ .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
+ .signatures = {"asdf", "qwer"},
+ },
+ {
+ .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
+ .outputName = "baz",
+ },
+ },
+ }))
+
+VERSIONED_CHARACTERIZATION_TEST(
+ WorkerProtoTest,
+ realisation_with_deps,
+ "realisation-with-deps",
+ defaultVersion,
+ (std::tuple{
Realisation{
{
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
@@ -172,7 +189,7 @@ VERSIONED_CHARACTERIZATION_TEST(
},
},
},
- DrvOutput{
+ {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
diff --git a/src/libstore/build-result.cc b/src/libstore/build-result.cc
index ecbd27b49..e3d9e9085 100644
--- a/src/libstore/build-result.cc
+++ b/src/libstore/build-result.cc
@@ -1,4 +1,6 @@
#include "nix/store/build-result.hh"
+#include "nix/util/json-utils.hh"
+#include
namespace nix {
@@ -11,4 +13,144 @@ std::strong_ordering BuildResult::Success::operator<=>(const BuildResult::Succes
bool BuildResult::Failure::operator==(const BuildResult::Failure &) const noexcept = default;
std::strong_ordering BuildResult::Failure::operator<=>(const BuildResult::Failure &) const noexcept = default;
+static constexpr std::array, 4> successStatusStrings{{
+#define ENUM_ENTRY(e) {BuildResult::Success::e, #e}
+ ENUM_ENTRY(Built),
+ ENUM_ENTRY(Substituted),
+ ENUM_ENTRY(AlreadyValid),
+ ENUM_ENTRY(ResolvesToAlreadyValid),
+#undef ENUM_ENTRY
+}};
+
+static std::string_view successStatusToString(BuildResult::Success::Status status)
+{
+ for (const auto & [enumVal, str] : successStatusStrings) {
+ if (enumVal == status)
+ return str;
+ }
+ throw Error("unknown success status: %d", static_cast(status));
+}
+
+static BuildResult::Success::Status successStatusFromString(std::string_view str)
+{
+ for (const auto & [enumVal, enumStr] : successStatusStrings) {
+ if (enumStr == str)
+ return enumVal;
+ }
+ throw Error("unknown built result success status '%s'", str);
+}
+
+static constexpr std::array, 12> failureStatusStrings{{
+#define ENUM_ENTRY(e) {BuildResult::Failure::e, #e}
+ ENUM_ENTRY(PermanentFailure),
+ ENUM_ENTRY(InputRejected),
+ ENUM_ENTRY(OutputRejected),
+ ENUM_ENTRY(TransientFailure),
+ ENUM_ENTRY(CachedFailure),
+ ENUM_ENTRY(TimedOut),
+ ENUM_ENTRY(MiscFailure),
+ ENUM_ENTRY(DependencyFailed),
+ ENUM_ENTRY(LogLimitExceeded),
+ ENUM_ENTRY(NotDeterministic),
+ ENUM_ENTRY(NoSubstituters),
+ ENUM_ENTRY(HashMismatch),
+#undef ENUM_ENTRY
+}};
+
+static std::string_view failureStatusToString(BuildResult::Failure::Status status)
+{
+ for (const auto & [enumVal, str] : failureStatusStrings) {
+ if (enumVal == status)
+ return str;
+ }
+ throw Error("unknown failure status: %d", static_cast(status));
+}
+
+static BuildResult::Failure::Status failureStatusFromString(std::string_view str)
+{
+ for (const auto & [enumVal, enumStr] : failureStatusStrings) {
+ if (enumStr == str)
+ return enumVal;
+ }
+ throw Error("unknown built result failure status '%s'", str);
+}
+
} // namespace nix
+
+namespace nlohmann {
+
+using namespace nix;
+
+void adl_serializer::to_json(json & res, const BuildResult & br)
+{
+ res = json::object();
+
+ // Common fields
+ res["timesBuilt"] = br.timesBuilt;
+ res["startTime"] = br.startTime;
+ res["stopTime"] = br.stopTime;
+
+ if (br.cpuUser.has_value()) {
+ res["cpuUser"] = br.cpuUser->count();
+ }
+ if (br.cpuSystem.has_value()) {
+ res["cpuSystem"] = br.cpuSystem->count();
+ }
+
+ // Handle success or failure variant
+ std::visit(
+ overloaded{
+ [&](const BuildResult::Success & success) {
+ res["success"] = true;
+ res["status"] = successStatusToString(success.status);
+ res["builtOutputs"] = success.builtOutputs;
+ },
+ [&](const BuildResult::Failure & failure) {
+ res["success"] = false;
+ res["status"] = failureStatusToString(failure.status);
+ res["errorMsg"] = failure.errorMsg;
+ res["isNonDeterministic"] = failure.isNonDeterministic;
+ },
+ },
+ br.inner);
+}
+
+BuildResult adl_serializer::from_json(const json & _json)
+{
+ auto & json = getObject(_json);
+
+ BuildResult br;
+
+ // Common fields
+ br.timesBuilt = getUnsigned(valueAt(json, "timesBuilt"));
+ br.startTime = getUnsigned(valueAt(json, "startTime"));
+ br.stopTime = getUnsigned(valueAt(json, "stopTime"));
+
+ if (auto cpuUser = optionalValueAt(json, "cpuUser")) {
+ br.cpuUser = std::chrono::microseconds(getUnsigned(*cpuUser));
+ }
+ if (auto cpuSystem = optionalValueAt(json, "cpuSystem")) {
+ br.cpuSystem = std::chrono::microseconds(getUnsigned(*cpuSystem));
+ }
+
+ // Determine success or failure based on success field
+ bool success = getBoolean(valueAt(json, "success"));
+ std::string statusStr = getString(valueAt(json, "status"));
+
+ if (success) {
+ BuildResult::Success s;
+ s.status = successStatusFromString(statusStr);
+ s.builtOutputs = valueAt(json, "builtOutputs");
+ br.inner = std::move(s);
+ } else {
+ BuildResult::Failure f;
+ f.status = failureStatusFromString(statusStr);
+ f.errorMsg = getString(valueAt(json, "errorMsg"));
+ f.isNonDeterministic = getBoolean(valueAt(json, "isNonDeterministic"));
+ br.inner = std::move(f);
+ }
+
+ return br;
+}
+
+} // namespace nlohmann
diff --git a/src/libstore/build/derivation-building-goal.cc b/src/libstore/build/derivation-building-goal.cc
index 164948390..c72130142 100644
--- a/src/libstore/build/derivation-building-goal.cc
+++ b/src/libstore/build/derivation-building-goal.cc
@@ -286,7 +286,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
PathSet lockFiles;
/* FIXME: Should lock something like the drv itself so we don't build same
CA drv concurrently */
- if (dynamic_cast(&worker.store)) {
+ if (auto * localStore = dynamic_cast(&worker.store)) {
/* If we aren't a local store, we might need to use the local store as
a build remote, but that would cause a deadlock. */
/* FIXME: Make it so we can use ourselves as a build remote even if we
@@ -296,9 +296,9 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
*/
for (auto & i : drv->outputsAndOptPaths(worker.store)) {
if (i.second.second)
- lockFiles.insert(worker.store.Store::toRealPath(*i.second.second));
+ lockFiles.insert(localStore->toRealPath(*i.second.second));
else
- lockFiles.insert(worker.store.Store::toRealPath(drvPath) + "." + i.first);
+ lockFiles.insert(localStore->toRealPath(drvPath) + "." + i.first);
}
}
@@ -331,12 +331,14 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
/* If any of the outputs already exist but are not valid, delete
them. */
- for (auto & [_, status] : initialOutputs) {
- if (!status.known || status.known->isValid())
- continue;
- auto storePath = status.known->path;
- debug("removing invalid path '%s'", worker.store.printStorePath(status.known->path));
- deletePath(worker.store.Store::toRealPath(storePath));
+ if (auto * localStore = dynamic_cast(&worker.store)) {
+ for (auto & [_, status] : initialOutputs) {
+ if (!status.known || status.known->isValid())
+ continue;
+ auto storePath = status.known->path;
+ debug("removing invalid path '%s'", worker.store.printStorePath(status.known->path));
+ deletePath(localStore->toRealPath(storePath));
+ }
}
/* Don't do a remote build if the derivation has the attribute
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index e6efd6c09..937946134 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -896,7 +896,7 @@ static void performOp(
auto path = WorkerProto::Serialise::read(*store, rconn);
logger->startWork();
logger->stopWork();
- dumpPath(store->toRealPath(path), conn.to);
+ store->narFromPath(path, conn.to);
break;
}
diff --git a/src/libstore/derivation-options.cc b/src/libstore/derivation-options.cc
index bd9704b44..75313841c 100644
--- a/src/libstore/derivation-options.cc
+++ b/src/libstore/derivation-options.cc
@@ -176,13 +176,26 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
return {};
};
- checks.allowedReferences = get_("allowedReferences");
- checks.allowedRequisites = get_("allowedRequisites");
- checks.disallowedReferences = get_("disallowedReferences").value_or(StringSet{});
- checks.disallowedRequisites = get_("disallowedRequisites").value_or(StringSet{});
- ;
-
- res.insert_or_assign(outputName, std::move(checks));
+ res.insert_or_assign(
+ outputName,
+ OutputChecks{
+ .maxSize = [&]() -> std::optional {
+ if (auto maxSize = get(output, "maxSize"))
+ return maxSize->get();
+ else
+ return std::nullopt;
+ }(),
+ .maxClosureSize = [&]() -> std::optional {
+ if (auto maxClosureSize = get(output, "maxClosureSize"))
+ return maxClosureSize->get();
+ else
+ return std::nullopt;
+ }(),
+ .allowedReferences = get_("allowedReferences"),
+ .disallowedReferences = get_("disallowedReferences").value_or(StringSet{}),
+ .allowedRequisites = get_("allowedRequisites"),
+ .disallowedRequisites = get_("disallowedRequisites").value_or(StringSet{}),
+ });
}
}
return res;
@@ -364,6 +377,7 @@ DerivationOptions adl_serializer::from_json(const json & json
.unsafeDiscardReferences = valueAt(json, "unsafeDiscardReferences"),
.passAsFile = getStringSet(valueAt(json, "passAsFile")),
+ .exportReferencesGraph = getMap(getObject(valueAt(json, "exportReferencesGraph")), getStringSet),
.additionalSandboxProfile = getString(valueAt(json, "additionalSandboxProfile")),
.noChroot = getBoolean(valueAt(json, "noChroot")),
@@ -396,6 +410,7 @@ void adl_serializer::to_json(json & json, const DerivationOpt
json["unsafeDiscardReferences"] = o.unsafeDiscardReferences;
json["passAsFile"] = o.passAsFile;
+ json["exportReferencesGraph"] = o.exportReferencesGraph;
json["additionalSandboxProfile"] = o.additionalSandboxProfile;
json["noChroot"] = o.noChroot;
@@ -423,6 +438,8 @@ DerivationOptions::OutputChecks adl_serializer:
return {
.ignoreSelfRefs = getBoolean(valueAt(json, "ignoreSelfRefs")),
+ .maxSize = ptrToOwned(getNullable(valueAt(json, "maxSize"))),
+ .maxClosureSize = ptrToOwned(getNullable(valueAt(json, "maxClosureSize"))),
.allowedReferences = ptrToOwned(getNullable(valueAt(json, "allowedReferences"))),
.disallowedReferences = getStringSet(valueAt(json, "disallowedReferences")),
.allowedRequisites = ptrToOwned(getNullable(valueAt(json, "allowedRequisites"))),
@@ -433,6 +450,8 @@ DerivationOptions::OutputChecks adl_serializer:
void adl_serializer::to_json(json & json, const DerivationOptions::OutputChecks & c)
{
json["ignoreSelfRefs"] = c.ignoreSelfRefs;
+ json["maxSize"] = c.maxSize;
+ json["maxClosureSize"] = c.maxClosureSize;
json["allowedReferences"] = c.allowedReferences;
json["disallowedReferences"] = c.disallowedReferences;
json["allowedRequisites"] = c.allowedRequisites;
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 726143db2..31ca167f9 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -1293,15 +1293,13 @@ void adl_serializer::to_json(json & res, const DerivationOutpu
overloaded{
[&](const DerivationOutput::InputAddressed & doi) { res["path"] = doi.path; },
[&](const DerivationOutput::CAFixed & dof) {
- /* it would be nice to output the path for user convenience, but
- this would require us to know the store dir. */
+ res = dof.ca;
+ // FIXME print refs?
+ /* it would be nice to output the path for user convenience, but
+ this would require us to know the store dir. */
#if 0
res["path"] = dof.path(store, drvName, outputName);
#endif
- res["method"] = std::string{dof.ca.method.render()};
- res["hashAlgo"] = printHashAlgo(dof.ca.hash.algo);
- res["hash"] = dof.ca.hash.to_string(HashFormat::Base16, false);
- // FIXME print refs?
},
[&](const DerivationOutput::CAFloating & dof) {
res["method"] = std::string{dof.method.render()};
@@ -1341,15 +1339,12 @@ adl_serializer::from_json(const json & _json, const Experiment
};
}
- else if (keys == (std::set{"method", "hashAlgo", "hash"})) {
- auto [method, hashAlgo] = methodAlgo();
+ else if (keys == (std::set{"method", "hash"})) {
auto dof = DerivationOutput::CAFixed{
- .ca =
- ContentAddress{
- .method = std::move(method),
- .hash = Hash::parseNonSRIUnprefixed(getString(valueAt(json, "hash")), hashAlgo),
- },
+ .ca = static_cast(_json),
};
+ if (dof.ca.method == ContentAddressMethod::Raw::Text)
+ xpSettings.require(Xp::DynamicDerivations, "text-hashed derivation output in JSON");
/* We no longer produce this (denormalized) field (for the
reasons described above), so we don't need to check it. */
#if 0
@@ -1392,7 +1387,7 @@ void adl_serializer::to_json(json & res, const Derivation & d)
res["name"] = d.name;
- res["version"] = 3;
+ res["version"] = 4;
{
nlohmann::json & outputsObj = res["outputs"];
@@ -1403,13 +1398,16 @@ void adl_serializer::to_json(json & res, const Derivation & d)
}
{
- auto & inputsList = res["inputSrcs"];
- inputsList = nlohmann::json ::array();
- for (auto & input : d.inputSrcs)
- inputsList.emplace_back(input);
- }
+ auto & inputsObj = res["inputs"];
+ inputsObj = nlohmann::json::object();
+
+ {
+ auto & inputsList = inputsObj["srcs"];
+ inputsList = nlohmann::json::array();
+ for (auto & input : d.inputSrcs)
+ inputsList.emplace_back(input);
+ }
- {
auto doInput = [&](this const auto & doInput, const auto & inputNode) -> nlohmann::json {
auto value = nlohmann::json::object();
value["outputs"] = inputNode.value;
@@ -1421,12 +1419,11 @@ void adl_serializer::to_json(json & res, const Derivation & d)
}
return value;
};
- {
- auto & inputDrvsObj = res["inputDrvs"];
- inputDrvsObj = nlohmann::json::object();
- for (auto & [inputDrv, inputNode] : d.inputDrvs.map) {
- inputDrvsObj[inputDrv.to_string()] = doInput(inputNode);
- }
+
+ auto & inputDrvsObj = inputsObj["drvs"];
+ inputDrvsObj = nlohmann::json::object();
+ for (auto & [inputDrv, inputNode] : d.inputDrvs.map) {
+ inputDrvsObj[inputDrv.to_string()] = doInput(inputNode);
}
}
@@ -1449,8 +1446,8 @@ Derivation adl_serializer::from_json(const json & _json, const Exper
res.name = getString(valueAt(json, "name"));
- if (valueAt(json, "version") != 3)
- throw Error("Only derivation format version 3 is currently supported.");
+ if (valueAt(json, "version") != 4)
+ throw Error("Only derivation format version 4 is currently supported.");
try {
auto outputs = getObject(valueAt(json, "outputs"));
@@ -1463,32 +1460,39 @@ Derivation adl_serializer::from_json(const json & _json, const Exper
}
try {
- auto inputSrcs = getArray(valueAt(json, "inputSrcs"));
- for (auto & input : inputSrcs)
- res.inputSrcs.insert(input);
- } catch (Error & e) {
- e.addTrace({}, "while reading key 'inputSrcs'");
- throw;
- }
+ auto inputsObj = getObject(valueAt(json, "inputs"));
- try {
- auto doInput = [&](this const auto & doInput, const auto & _json) -> DerivedPathMap::ChildNode {
- auto & json = getObject(_json);
- DerivedPathMap::ChildNode node;
- node.value = getStringSet(valueAt(json, "outputs"));
- auto drvs = getObject(valueAt(json, "dynamicOutputs"));
- for (auto & [outputId, childNode] : drvs) {
- xpSettings.require(
- Xp::DynamicDerivations, [&] { return fmt("dynamic output '%s' in JSON", outputId); });
- node.childMap[outputId] = doInput(childNode);
- }
- return node;
- };
- auto drvs = getObject(valueAt(json, "inputDrvs"));
- for (auto & [inputDrvPath, inputOutputs] : drvs)
- res.inputDrvs.map[StorePath{inputDrvPath}] = doInput(inputOutputs);
+ try {
+ auto inputSrcs = getArray(valueAt(inputsObj, "srcs"));
+ for (auto & input : inputSrcs)
+ res.inputSrcs.insert(input);
+ } catch (Error & e) {
+ e.addTrace({}, "while reading key 'srcs'");
+ throw;
+ }
+
+ try {
+ auto doInput = [&](this const auto & doInput, const auto & _json) -> DerivedPathMap::ChildNode {
+ auto & json = getObject(_json);
+ DerivedPathMap::ChildNode node;
+ node.value = getStringSet(valueAt(json, "outputs"));
+ auto drvs = getObject(valueAt(json, "dynamicOutputs"));
+ for (auto & [outputId, childNode] : drvs) {
+ xpSettings.require(
+ Xp::DynamicDerivations, [&] { return fmt("dynamic output '%s' in JSON", outputId); });
+ node.childMap[outputId] = doInput(childNode);
+ }
+ return node;
+ };
+ auto drvs = getObject(valueAt(inputsObj, "drvs"));
+ for (auto & [inputDrvPath, inputOutputs] : drvs)
+ res.inputDrvs.map[StorePath{inputDrvPath}] = doInput(inputOutputs);
+ } catch (Error & e) {
+ e.addTrace({}, "while reading key 'drvs'");
+ throw;
+ }
} catch (Error & e) {
- e.addTrace({}, "while reading key 'inputDrvs'");
+ e.addTrace({}, "while reading key 'inputs'");
throw;
}
diff --git a/src/libstore/include/nix/store/build-result.hh b/src/libstore/include/nix/store/build-result.hh
index 0446c4038..4739232f8 100644
--- a/src/libstore/include/nix/store/build-result.hh
+++ b/src/libstore/include/nix/store/build-result.hh
@@ -7,6 +7,7 @@
#include "nix/store/derived-path.hh"
#include "nix/store/realisation.hh"
+#include "nix/util/json-impls.hh"
namespace nix {
@@ -175,3 +176,5 @@ struct KeyedBuildResult : BuildResult
};
} // namespace nix
+
+JSON_IMPL(nix::BuildResult)
diff --git a/src/libstore/include/nix/store/build/derivation-builder.hh b/src/libstore/include/nix/store/build/derivation-builder.hh
index 5fad26e83..5eed38462 100644
--- a/src/libstore/include/nix/store/build/derivation-builder.hh
+++ b/src/libstore/include/nix/store/build/derivation-builder.hh
@@ -62,7 +62,7 @@ struct DerivationBuilderParams
/**
* The derivation stored at drvPath.
*/
- const Derivation & drv;
+ const BasicDerivation & drv;
/**
* The derivation options of `drv`.
diff --git a/src/libstore/include/nix/store/globals.hh b/src/libstore/include/nix/store/globals.hh
index 8aa82c4a2..5ddfbee30 100644
--- a/src/libstore/include/nix/store/globals.hh
+++ b/src/libstore/include/nix/store/globals.hh
@@ -189,7 +189,7 @@ public:
0,
"cores",
R"(
- Sets the value of the `NIX_BUILD_CORES` environment variable in the [invocation of the `builder` executable](@docroot@/language/derivations.md#builder-execution) of a derivation.
+ Sets the value of the `NIX_BUILD_CORES` environment variable in the [invocation of the `builder` executable](@docroot@/store/building.md#builder-execution) of a derivation.
The `builder` executable can use this variable to control its own maximum amount of parallelism.