Compare commits

..

3 Commits

Author SHA1 Message Date
John Ericson
156061409a Do some parsing of git hashes based on length.
Note that for Nix-native information we should *not* do length tricks,
but instead always rely on an explicit algorithm. This hack should be
only for foreign hash literals.
2025-11-09 18:52:35 -05:00
John Ericson
6da56d7ed4 Make changes to support libgit2 experimental API
This experimental interface is likely to become stable with libgit2 2.0.
2025-11-09 18:52:35 -05:00
John Ericson
9846305fee Switch to libgit2 experimental
Just patch the package for now, but we should make Nixpkgs provide what
we need (whether that the experimental variant, or a 2.x release with
the new API made non-experimental) before releasing this change.
2025-11-09 18:52:35 -05:00
385 changed files with 2618 additions and 9438 deletions

View File

@@ -20,7 +20,7 @@ jobs:
with:
app-id: ${{ vars.CI_APP_ID }}
private-key: ${{ secrets.CI_APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
# required to find all branches

View File

@@ -24,7 +24,7 @@ jobs:
eval:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: ./.github/actions/install-nix-action
@@ -40,7 +40,7 @@ jobs:
name: pre-commit checks
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: ./.github/actions/install-nix-action
with:
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
@@ -87,7 +87,7 @@ jobs:
runs-on: ${{ matrix.runs-on }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: ./.github/actions/install-nix-action
@@ -162,7 +162,7 @@ jobs:
name: installer test ${{ matrix.scenario }}
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Download installer tarball
uses: actions/download-artifact@v6
with:
@@ -174,7 +174,7 @@ jobs:
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
TARBALL_PATH="$(find "$GITHUB_WORKSPACE/out" -name 'nix*.tar.xz' -print | head -n 1)"
echo "tarball-path=file://$TARBALL_PATH" >> "$GITHUB_OUTPUT"
- uses: cachix/install-nix-action@0b0e072294b088b73964f1d72dfdac0951439dbd # v31.8.4
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31.8.2
if: ${{ !matrix.experimental-installer }}
with:
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
@@ -227,7 +227,7 @@ jobs:
github.ref_name == 'master'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: ./.github/actions/install-nix-action
@@ -276,14 +276,14 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout nix
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Checkout flake-regressions
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
repository: NixOS/flake-regressions
path: flake-regressions
- name: Checkout flake-regressions-data
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
repository: NixOS/flake-regressions-data
path: flake-regressions/tests
@@ -303,7 +303,7 @@ jobs:
github.event_name == 'push' &&
github.ref_name == 'master'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: ./.github/actions/install-nix-action

View File

@@ -88,7 +88,7 @@ manual = custom_target(
@0@ @INPUT0@ @CURRENT_SOURCE_DIR@ > @DEPFILE@
@0@ @INPUT1@ summary @2@ < @CURRENT_SOURCE_DIR@/source/SUMMARY.md.in > @2@/source/SUMMARY.md
sed -e 's|@version@|@3@|g' < @INPUT2@ > @2@/book.toml
@4@ -r -L --exclude='*.drv' --include='*.md' @CURRENT_SOURCE_DIR@/ @2@/
@4@ -r -L --include='*.md' @CURRENT_SOURCE_DIR@/ @2@/
(cd @2@; RUST_LOG=warn @1@ build -d @2@ 3>&2 2>&1 1>&3) | { grep -Fv "because fragment resolution isn't implemented" || :; } 3>&2 2>&1 1>&3
rm -rf @2@/manual
mv @2@/html @2@/manual

View File

@@ -37,17 +37,14 @@ mkMesonDerivation (finalAttrs: {
(fileset.unions [
../../.version
# For example JSON
../../src/libutil-tests/data/memory-source-accessor
../../src/libutil-tests/data/hash
../../src/libstore-tests/data/content-address
../../src/libstore-tests/data/store-path
../../src/libstore-tests/data/realisation
../../src/libstore-tests/data/derivation
../../src/libstore-tests/data/derived-path
../../src/libstore-tests/data/path-info
../../src/libstore-tests/data/nar-info
../../src/libstore-tests/data/build-result
../../src/libstore-tests/data/dummy-store
# Too many different types of files to filter for now
../../doc/manual
./.
@@ -61,8 +58,8 @@ mkMesonDerivation (finalAttrs: {
"man"
];
nativeBuildInputs = [
nix-cli
# Hack for sake of the dev shell
passthru.externalNativeBuildInputs = [
meson
ninja
(lib.getBin lowdown-unsandboxed)
@@ -81,6 +78,10 @@ mkMesonDerivation (finalAttrs: {
changelog-d
];
nativeBuildInputs = finalAttrs.passthru.externalNativeBuildInputs ++ [
nix-cli
];
preConfigure = ''
chmod u+w ./.version
echo ${finalAttrs.version} > ./.version

View File

@@ -22,15 +22,7 @@ The store path info JSON format has been updated from version 1 to version 2:
- New: `"ca": {"method": "nar", "hash": {"algorithm": "sha256", "format": "base64", "hash": "EMIJ+giQ..."}}`
- Still `null` values for input-addressed store objects
- **Structured hash fields**:
Hash values (`narHash` and `downloadHash`) are now structured JSON objects instead of strings:
- Old: `"narHash": "sha256:FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="`
- New: `"narHash": {"algorithm": "sha256", "format": "base64", "hash": "FePFYIlM..."}`
- Same structure applies to `downloadHash` in NAR info contexts
Nix currently only produces, and doesn't consume this format.
Version 1 format is still accepted when reading for backward compatibility.
**Affected command**: `nix path-info --json`

View File

@@ -1,12 +0,0 @@
---
synopsis: Fix "download buffer is full; consider increasing the 'download-buffer-size' setting" warning
prs: [14614]
issues: [11728]
---
The underlying issue that led to [#11728](https://github.com/NixOS/nix/issues/11728) has been resolved by utilizing
[libcurl write pausing functionality](https://curl.se/libcurl/c/curl_easy_pause.html) to control backpressure when unpacking to slow destinations like the git-backed tarball cache. The default value of `download-buffer-size` is now 1 MiB and it's no longer recommended to increase it, since the root cause has been fixed.
This is expected to improve download performance on fast connections, since previously a single slow download consumer would stall the thread and prevent any other transfers from progressing.
Many thanks go out to the [Lix project](https://lix.systems/) for the [implementation](https://git.lix.systems/lix-project/lix/commit/4ae6fb5a8f0d456b8d2ba2aaca3712b4e49057fc) that served as inspiration for this change and for triaging libcurl [issues with pausing](https://github.com/curl/curl/issues/19334).

View File

@@ -1,21 +0,0 @@
---
synopsis: "S3 binary cache stores now support storage class configuration"
prs: [14464]
issues: [7015]
---
S3 binary cache stores now support configuring the storage class for uploaded objects via the `storage-class` parameter. This allows users to optimize costs by selecting appropriate storage tiers based on access patterns.
Example usage:
```bash
# Use Glacier storage for long-term archival
nix copy --to 's3://my-bucket?storage-class=GLACIER' /nix/store/...
# Use Intelligent Tiering for automatic cost optimization
nix copy --to 's3://my-bucket?storage-class=INTELLIGENT_TIERING' /nix/store/...
```
The storage class applies to both regular uploads and multipart uploads. When not specified, objects use the bucket's default storage class.
See the [S3 storage classes documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html) for available storage classes and their characteristics.

View File

@@ -29,7 +29,6 @@
- [Build Trace](store/build-trace.md)
- [Derivation Resolution](store/resolution.md)
- [Building](store/building.md)
- [Secrets](store/secrets.md)
- [Store Types](store/types/index.md)
{{#include ./store/types/SUMMARY.md}}
- [Appendix: Math notation](store/math-notation.md)
@@ -121,17 +120,14 @@
- [Architecture and Design](architecture/architecture.md)
- [Formats and Protocols](protocols/index.md)
- [JSON Formats](protocols/json/index.md)
- [File System Object](protocols/json/file-system-object.md)
- [Hash](protocols/json/hash.md)
- [Content Address](protocols/json/content-address.md)
- [Store Path](protocols/json/store-path.md)
- [Store Object Info](protocols/json/store-object-info.md)
- [Derivation](protocols/json/derivation/index.md)
- [Derivation Options](protocols/json/derivation/options.md)
- [Derivation](protocols/json/derivation.md)
- [Deriving Path](protocols/json/deriving-path.md)
- [Build Trace Entry](protocols/json/build-trace-entry.md)
- [Build Result](protocols/json/build-result.md)
- [Store](protocols/json/store.md)
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
- [Store Path Specification](protocols/store-path.md)
- [Nix Archive (NAR) Format](protocols/nix-archive/index.md)

View File

@@ -36,7 +36,7 @@ to a temporary location. The tarball must include a single top-level
directory containing at least a file named `default.nix`.
`nix-build` is essentially a wrapper around
[`nix-instantiate`](./nix-instantiate.md) (to translate a high-level Nix
[`nix-instantiate`](nix-instantiate.md) (to translate a high-level Nix
expression to a low-level [store derivation]) and [`nix-store
--realise`](@docroot@/command-ref/nix-store/realise.md) (to build the store
derivation).
@@ -52,8 +52,8 @@ derivation).
# Options
All options not listed here are passed to
[`nix-store --realise`](./nix-store/realise.md),
except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](./nix-instantiate.md).
[`nix-store --realise`](nix-store/realise.md),
except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](nix-instantiate.md).
- <span id="opt-no-out-link">[`--no-out-link`](#opt-no-out-link)<span>

View File

@@ -32,7 +32,7 @@ standard input.
- `--add-root` *path*
See the [corresponding option](./nix-store.md) in `nix-store`.
See the [corresponding option](nix-store.md) in `nix-store`.
- `--parse`

View File

@@ -15,7 +15,7 @@ In the development shell, set the `mesonBuildType` environment variable to `debu
Then, proceed to build Nix as described in [Building Nix](./building.md).
This will build Nix with debug symbols, which are essential for effective debugging.
It is also possible to build without optimization for faster build:
It is also possible to build without debugging for faster build:
```console
[nix-shell]$ NIX_HARDENING_ENABLE=$(printLines $NIX_HARDENING_ENABLE | grep -v fortify)

View File

@@ -137,12 +137,6 @@ $ _NIX_TEST_ACCEPT=1 meson test nix-store-tests -v
will regenerate the "golden master" expected result for the `libnixstore` characterisation tests.
The characterisation tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything.
### JSON Schema testing
In `doc/manual/source/protocols/json/` we have a number of manual pages generated from [JSON Schema](https://json-schema.org/).
That JSON schema is tested against the JSON file test data used in [characterisation tests](#characterisation-testing-unit ) for JSON (de)serialization, in `src/json-schema-checks`.
Between the JSON (de)serialization testing, and this testing of the same data against the schema, we make sure that the manual, the implementation, and a machine-readable schema are are all in sync.
### Unit test support libraries
There are headers and code which are not just used to test the library in question, but also downstream libraries.

View File

@@ -0,0 +1,7 @@
{{#include derivation-v4-fixed.md}}
<!-- need to convert YAML to JSON first
## Raw Schema
[JSON Schema for Derivation v3](schema/derivation-v4.json)
-->

View File

@@ -1,7 +0,0 @@
{{#include ../derivation-v4-fixed.md}}
<!-- need to convert YAML to JSON first
## Raw Schema
[JSON Schema for Derivation v4](schema/derivation-v4.json)
-->

View File

@@ -1,49 +0,0 @@
{{#include ../derivation-options-v1-fixed.md}}
## Examples
### Input-addressed derivations
#### Default options
```json
{{#include ../schema/derivation-options-v1/ia/defaults.json}}
```
#### All options set
```json
{{#include ../schema/derivation-options-v1/ia/all_set.json}}
```
#### Default options (structured attributes)
```json
{{#include ../schema/derivation-options-v1/ia/structuredAttrs_defaults.json}}
```
#### All options set (structured attributes)
```json
{{#include ../schema/derivation-options-v1/ia/structuredAttrs_all_set.json}}
```
### Content-addressed derivations
#### All options set
```json
{{#include ../schema/derivation-options-v1/ca/all_set.json}}
```
#### All options set (structured attributes)
```json
{{#include ../schema/derivation-options-v1/ca/structuredAttrs_all_set.json}}
```
<!-- need to convert YAML to JSON first
## Raw Schema
[JSON Schema for Derivation Options v1](schema/derivation-options-v1.json)
-->

View File

@@ -1,21 +0,0 @@
{{#include file-system-object-v1-fixed.md}}
## Examples
### Simple
```json
{{#include schema/file-system-object-v1/simple.json}}
```
### Complex
```json
{{#include schema/file-system-object-v1/complex.json}}
```
<!-- need to convert YAML to JSON first
## Raw Schema
[JSON Schema for File System Object v1](schema/file-system-object-v1.json)
-->

View File

@@ -11,8 +11,7 @@ s/\\`/`/g
#
# As we have more such relative links, more replacements of this nature
# should appear below.
s^#/\$defs/\(regular\|symlink\|directory\)^In this schema^g
s^\(./hash-v1.yaml\)\?#/$defs/algorithm^[JSON format for `Hash`](@docroot@/protocols/json/hash.html#algorithm)^g
s^\(./hash-v1.yaml\)^[JSON format for `Hash`](@docroot@/protocols/json/hash.html)^g
s^\(./content-address-v1.yaml\)\?#/$defs/method^[JSON format for `ContentAddress`](@docroot@/protocols/json/content-address.html#method)^g
s^\(./content-address-v1.yaml\)^[JSON format for `ContentAddress`](@docroot@/protocols/json/content-address.html)^g
s^\(./hash-v1.yaml\)\?#/$defs/algorithm^[JSON format for `Hash`](./hash.html#algorithm)^g
s^\(./hash-v1.yaml\)^[JSON format for `Hash`](./hash.html)^g
s^\(./content-address-v1.yaml\)\?#/$defs/method^[JSON format for `ContentAddress`](./content-address.html#method)^g
s^\(./content-address-v1.yaml\)^[JSON format for `ContentAddress`](./content-address.html)^g

View File

@@ -9,17 +9,14 @@ json_schema_for_humans = find_program('generate-schema-doc', required : false)
json_schema_config = files('json-schema-for-humans-config.yaml')
schemas = [
'file-system-object-v1',
'hash-v1',
'content-address-v1',
'store-path-v1',
'store-object-info-v2',
'derivation-v4',
'derivation-options-v1',
'deriving-path-v1',
'build-trace-entry-v1',
'build-result-v1',
'store-v1',
]
schema_files = files()

View File

@@ -4,97 +4,71 @@ 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).
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
allOf:
- "$ref": "#/$defs/key"
- "$ref": "#/$defs/value"
properties:
id: {}
outPath: {}
dependentRealisations: {}
signatures: {}
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
"$defs":
key:
title: Build Trace Key
description: |
A [build trace entry](@docroot@/store/build-trace.md) is a key-value pair.
This is the "key" part, refering to a derivation and output.
type: object
required:
- id
properties:
id:
type: string
title: Derivation Output ID
pattern: "^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$"
description: |
Unique identifier for the derivation output that was built.
Format: `{hash-quotient-drv}!{output-name}`
- **hash-quotient-drv**: SHA-256 [hash of the quotient derivation](@docroot@/store/derivation/outputs/input-address.md#hash-quotient-drv).
Begins with `sha256:`.
- **output-name**: Name of the specific output (e.g., "out", "dev", "doc")
Example: `"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo"`
value:
title: Build Trace Value
description: |
A [build trace entry](@docroot@/store/build-trace.md) is a key-value pair.
This is the "value" part, describing an output.
type: object
required:
- outPath
- dependentRealisations
- signatures
properties:
outPath:
"$ref": "store-path-v1.yaml"
title: Output Store Path
description: |
The path to the store object that resulted from building this derivation for the given output name.
dependentRealisations:
type: object
title: Underlying Base Build Trace
description: |
This is for [*derived*](@docroot@/store/build-trace.md#derived) build trace entries to ensure coherence.
Keys are derivation output IDs (same format as the main `id` field).
Values are the store paths that those dependencies resolved to.
As described in the linked section on derived build trace traces, derived build trace entries must be kept in addition and not instead of the underlying base build entries.
This is the set of base build trace entries that this derived build trace is derived from.
(The set is also a map since this miniature base build trace must be coherent, mapping each key to a single value.)
patternProperties:
"^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$":
"$ref": "store-path-v1.yaml"
title: Dependent Store Path
description: Store path that this dependency resolved to during the build
additionalProperties: false
signatures:
type: array
title: Build Signatures
description: |
A set of cryptographic signatures attesting to the authenticity of this build trace entry.
items:
type: string
title: Signature
description: A single cryptographic signature

View File

@@ -1 +0,0 @@
../../../../../../src/libstore-tests/data/derivation

View File

@@ -1,242 +0,0 @@
"$schema": "http://json-schema.org/draft-04/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/derivation-options-v1.json"
title: Derivation Options
description: |
JSON representation of Nix's `DerivationOptions` type.
This schema describes various build-time options and constraints that can be specified for a derivation.
> **Warning**
>
> This JSON format is currently
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
> and subject to change.
type: object
required:
- outputChecks
- unsafeDiscardReferences
- passAsFile
- exportReferencesGraph
- additionalSandboxProfile
- noChroot
- impureHostDeps
- impureEnvVars
- allowLocalNetworking
- requiredSystemFeatures
- preferLocalBuild
- allowSubstitutes
properties:
outputChecks:
type: object
title: Output Check
description: |
Constraints on what the derivation's outputs can and cannot reference.
Can either apply to all outputs or be specified per output.
oneOf:
- title: Output Checks For All Outputs
description: |
Output checks that apply to all outputs of the derivation.
required:
- forAllOutputs
properties:
forAllOutputs:
"$ref": "#/$defs/outputCheckSpec"
additionalProperties: false
- title: Output Checks Per Output
description: |
Output checks specified individually for each output.
required:
- perOutput
properties:
perOutput:
type: object
additionalProperties:
"$ref": "#/$defs/outputCheckSpec"
additionalProperties: false
unsafeDiscardReferences:
type: object
title: Unsafe Discard References
description: |
A map specifying which references should be unsafely discarded from each output.
This is generally not recommended and requires special permissions.
additionalProperties:
type: array
items:
type: string
passAsFile:
type: array
title: Pass As File
description: |
List of environment variable names whose values should be passed as files rather than directly.
items:
type: string
exportReferencesGraph:
type: object
title: Export References Graph
description: |
Specify paths whose references graph should be exported to files.
additionalProperties:
type: array
items:
"$ref": "deriving-path-v1.yaml"
additionalSandboxProfile:
type: string
title: Additional Sandbox Profile
description: |
Additional sandbox profile directives (macOS specific).
noChroot:
type: boolean
title: No Chroot
description: |
Whether to disable the build sandbox, if allowed.
impureHostDeps:
type: array
title: Impure Host Dependencies
description: |
List of host paths that the build can access.
items:
type: string
impureEnvVars:
type: array
title: Impure Environment Variables
description: |
List of environment variable names that should be passed through to the build from the calling environment.
items:
type: string
allowLocalNetworking:
type: boolean
title: Allow Local Networking
description: |
Whether the build should have access to local network (macOS specific).
requiredSystemFeatures:
type: array
title: Required System Features
description: |
List of system features required to build this derivation (e.g., "kvm", "nixos-test").
items:
type: string
preferLocalBuild:
type: boolean
title: Prefer Local Build
description: |
Whether this derivation should preferably be built locally rather than its outputs substituted.
allowSubstitutes:
type: boolean
title: Allow Substitutes
description: |
Whether substituting from other stores should be allowed for this derivation's outputs.
additionalProperties: false
$defs:
outputCheckSpec:
type: object
title: Output Check Specification
description: |
Constraints on what a specific output can reference.
required:
- ignoreSelfRefs
- maxSize
- maxClosureSize
- allowedReferences
- allowedRequisites
- disallowedReferences
- disallowedRequisites
properties:
ignoreSelfRefs:
type: boolean
title: Ignore Self References
description: |
Whether references from this output to itself should be ignored when checking references.
maxSize:
type: ["integer", "null"]
title: Maximum Size
description: |
Maximum allowed size of this output in bytes, or null for no limit.
minimum: 0
maxClosureSize:
type: ["integer", "null"]
title: Maximum Closure Size
description: |
Maximum allowed size of this output's closure in bytes, or null for no limit.
minimum: 0
allowedReferences:
oneOf:
- type: array
items:
"$ref": "#/$defs/drvRef"
- type: "null"
title: Allowed References
description: |
If set, the output can only reference paths in this list.
If null, no restrictions apply.
allowedRequisites:
oneOf:
- type: array
items:
"$ref": "#/$defs/drvRef"
- type: "null"
title: Allowed Requisites
description: |
If set, the output's closure can only contain paths in this list.
If null, no restrictions apply.
disallowedReferences:
type: array
title: Disallowed References
description: |
The output must not reference any paths in this list.
items:
"$ref": "#/$defs/drvRef"
disallowedRequisites:
type: array
title: Disallowed Requisites
description: |
The output's closure must not contain any paths in this list.
items:
"$ref": "#/$defs/drvRef"
additionalProperties: false
drvRef:
# TODO fix bug in checker, should be `oneOf`
anyOf:
- type: object
title: Current derivation Output Reference
description: |
A reference to a specific output of the current derivation.
required:
- drvPath
- output
properties:
drvPath:
type: string
const: "self"
title: This derivation
description: |
Won't be confused for a deriving path
output:
type: string
title: Output Name
description: |
The name of the output being referenced.
additionalProperties: false
- "$ref": "deriving-path-v1.yaml"

View File

@@ -1 +0,0 @@
../../../../../../src/libutil-tests/data/memory-source-accessor

View File

@@ -1,71 +0,0 @@
"$schema": http://json-schema.org/draft-04/schema#
"$id": https://nix.dev/manual/nix/latest/protocols/json/schema/file-system-object-v1.json
title: File System Object
description: |
This schema describes the JSON representation of Nix's [File System Object](@docroot@/store/file-system-object.md).
The schema is recursive because file system objects contain other file system objects.
type: object
required: ["type"]
properties:
type:
type: string
enum: ["regular", "symlink", "directory"]
# Enforce conditional structure based on `type`
anyOf:
- $ref: "#/$defs/regular"
required: ["type", "contents"]
- $ref: "#/$defs/directory"
required: ["type", "entries"]
- $ref: "#/$defs/symlink"
required: ["type", "target"]
"$defs":
regular:
title: Regular File
description: |
See [Regular File](@docroot@/store/file-system-object.md#regular) in the manual for details.
required: ["contents"]
properties:
type:
const: "regular"
contents:
type: string
description: File contents
executable:
type: boolean
description: Whether the file is executable.
default: false
additionalProperties: false
directory:
title: Directory
description: |
See [Directory](@docroot@/store/file-system-object.md#directory) in the manual for details.
required: ["entries"]
properties:
type:
const: "directory"
entries:
type: object
description: |
Map of names to nested file system objects (for type=directory)
additionalProperties:
$ref: "#"
additionalProperties: false
symlink:
title: Symbolic Link
description: |
See [Symbolic Link](@docroot@/store/file-system-object.md#symlink) in the manual for details.
required: ["target"]
properties:
type:
const: "symlink"
target:
type: string
description: Target path of the symlink.
additionalProperties: false

View File

@@ -71,7 +71,7 @@ $defs:
Note: This field may not be present in all contexts, such as when the path is used as the key and the the store object info the value in map.
narHash:
"$ref": "./hash-v1.yaml"
type: string
title: NAR Hash
description: |
Hash of the [file system object](@docroot@/store/file-system-object.md) part of the store object when serialized as a [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive).
@@ -229,7 +229,7 @@ $defs:
> This is an impure "`.narinfo`" field that may not be included in certain contexts.
downloadHash:
"$ref": "./hash-v1.yaml"
type: string
title: Download Hash
description: |
A digest for the compressed archive itself, as opposed to the data contained within.

View File

@@ -1 +0,0 @@
../../../../../../src/libstore-tests/data/dummy-store

View File

@@ -1,90 +0,0 @@
"$schema": "http://json-schema.org/draft-04/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-v1.json"
title: Store
description: |
Experimental JSON representation of a Nix [Store](@docroot@/store/index.md).
This schema describes the JSON serialization of a Nix store.
We use it for (de)serializing in-memory "dummy stores" used for testing, but in principle the data represented in this schema could live in any type of store.
> **Warning**
>
> This JSON format is currently
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
> and subject to change.
type: object
required:
- config
- contents
- derivations
- buildTrace
properties:
config:
"$ref": "#/$defs/storeConfig"
contents:
type: object
title: Store Objects
description: |
Map of [store path](@docroot@/store/store-path.md) base names to [store objects](@docroot@/store/store-object.md).
patternProperties:
"^[0123456789abcdfghijklmnpqrsvwxyz]{32}-.+$":
type: object
title: Store Object
required:
- info
- contents
properties:
info:
"$ref": "./store-object-info-v2.yaml#/$defs/impure"
title: Store Object Info
description: |
Metadata about the [store object](@docroot@/store/store-object.md) including hash, size, references, etc.
contents:
"$ref": "./file-system-object-v1.yaml"
title: File System Object Contents
description: |
The actual [file system object](@docroot@/store/file-system-object.md) contents of this store path.
additionalProperties: false
additionalProperties: false
derivations:
type: object
title: Derivations
description: |
Map of [store path](@docroot@/store/store-path.md) base names (always ending in `.drv`) to [derivations](@docroot@/store/derivation/index.md).
patternProperties:
"^[0123456789abcdfghijklmnpqrsvwxyz]{32}-.+\\.drv$":
"$ref": "./derivation-v4.yaml"
additionalProperties: false
buildTrace:
type: object
title: Build Trace
description: |
Map of output hashes (base64 SHA256) to maps of output names to realisations.
Records which outputs have been built and their realisations.
See [Build Trace](@docroot@/store/build-trace.md) for more details.
patternProperties:
"^[A-Za-z0-9+/]{43}=$":
type: object
additionalProperties:
"$ref": "./build-trace-entry-v1.yaml#/$defs/value"
additionalProperties: false
"$defs":
storeConfig:
title: Store Configuration
description: |
Configuration for the store, including the store directory path.
type: object
required:
- store
properties:
store:
type: string
title: Store Directory
description: |
The store directory path (e.g., `/nix/store`).
additionalProperties: false

View File

@@ -1,21 +0,0 @@
{{#include store-v1-fixed.md}}
## Examples
### Empty store
```json
{{#include schema/store-v1/empty.json}}
```
### Store with one file
```json
{{#include schema/store-v1/one-flat-file.json}}
```
### Store with one derivation
```json
{{#include schema/store-v1/one-derivation.json}}
```

View File

@@ -24,7 +24,7 @@ nar-obj-inner
| str("type"), str("directory") directory
;
regular = [ str("executable"), str("") ], str("contents"), str(contents);
regular = [ str("executable") ], str("contents"), str(contents);
symlink = str("target"), str(target);
@@ -52,4 +52,4 @@ The Nix Archive (NAR) format is also formally described using [Kaitai Struct](ht
{{#include nar.ksy}}
```
The source of the spec can be found [here](https://github.com/nixos/nix/blob/master/src/nix-manual/source/protocols/nix-archive/nar.ksy). Contributions and improvements to the spec are welcomed.
The source of the spec can be found [here](https://github.com/nixos/nix/blob/master/src/nix-manual/source/protocols/nix-archive/nar.ksy). Contributions and improvements to the spec are welcomed.

View File

@@ -12,7 +12,7 @@
We ultimately want to rectify this issue with all JSON formats to the extent allowed by our stability promises. To start with, we are changing the JSON format for derivations because the `nix derivation` commands are — in addition to being formally unstable — less widely used than other unstable commands.
See the documentation on the [JSON format for derivations](@docroot@/protocols/json/derivation/index.md) for further details.
See the documentation on the [JSON format for derivations](@docroot@/protocols/json/derivation.md) for further details.
- C API: `nix_get_attr_name_byidx`, `nix_get_attr_byidx` take a `nix_value *` instead of `const nix_value *` [#13987](https://github.com/NixOS/nix/pull/13987)

View File

@@ -192,7 +192,7 @@ There are two formats, documented separately:
- The legacy ["ATerm" format](@docroot@/protocols/derivation-aterm.md)
- The experimental, currently under development and changing [JSON format](@docroot@/protocols/json/derivation/index.md)
- The experimental, currently under development and changing [JSON format](@docroot@/protocols/json/derivation.md)
Every derivation has a canonical choice of encoding used to serialize it to a store object.
This ensures that there is a canonical [store path] used to refer to the derivation, as described in [Referencing derivations](#derivation-path).

View File

@@ -3,23 +3,19 @@
Nix uses a simplified model of the file system, which consists of file system objects.
Every file system object is one of the following:
- [**Regular File**]{#regular}
- File
- A possibly empty sequence of bytes for contents
- A single boolean representing the [executable](https://en.m.wikipedia.org/wiki/File-system_permissions#Permissions) permission
- [**Directory**]{#directory}
- Directory
Mapping of names to child file system objects
- [**Symbolic link**]{#symlink}
- [Symbolic link](https://en.m.wikipedia.org/wiki/Symbolic_link)
An arbitrary string, known as the *target* of the symlink.
In general, Nix does not assign any semantics to symbolic links.
Certain operations however, may make additional assumptions and attempt to use the target to find another file system object.
> See [the Wikpedia article on symbolic links](https://en.m.wikipedia.org/wiki/Symbolic_link) for background information if you are unfamiliar with this Unix concept.
An arbitrary string.
Nix does not assign any semantics to symbolic links.
File system objects and their children form a tree.
A bare file or symlink can be a root file system object.

View File

@@ -1,20 +0,0 @@
# Secrets
The store is readable to all users on the system. For this reason, it
is generally discouraged to allow secrets to make it into the store.
Even on a single-user system, separate system users isolate services
from each other and having secrets that all local users can read
weakens that isolation. When using external store caches the secrets
may end up there, and on multi-user systems the secrets will be
available to all those users.
Organize your derivations so that secrets are read from the filesystem
(with appropriate access controls) at run time. Place the secrets on
the filesystem manually or use a scheme that includes the secret in
the store in encrypted form, and decrypts it adding the relevant
access control on system activation.
Several such schemes for NixOS can in the
[comparison of secret managing schemes] on the wiki.
[comparison of secret managing schemes]: https://wiki.nixos.org/wiki/Comparison_of_secret_managing_schemes

View File

@@ -79,8 +79,6 @@
# Not supported by nixfmt
''^tests/functional/lang/eval-okay-deprecate-cursed-or\.nix$''
''^tests/functional/lang/eval-okay-attrs5\.nix$''
''^tests/functional/lang/eval-fail-dynamic-attrs-inherit\.nix$''
''^tests/functional/lang/eval-fail-dynamic-attrs-inherit-2\.nix$''
# More syntax tests
# These tests, or parts of them, should have been parse-* test cases.

View File

@@ -60,9 +60,4 @@ if get_option('unit-tests')
subproject('libflake-tests')
endif
subproject('nix-functional-tests')
if get_option('json-schema-checks')
subproject('json-schema-checks')
endif
if get_option('kaitai-struct-checks')
subproject('kaitai-struct-checks')
endif
subproject('json-schema-checks')

View File

@@ -27,17 +27,3 @@ option(
value : false,
description : 'Build benchmarks (requires gbenchmark)',
)
option(
'kaitai-struct-checks',
type : 'boolean',
value : true,
description : 'Check the Kaitai Struct specifications (requires Kaitai Struct)',
)
option(
'json-schema-checks',
type : 'boolean',
value : true,
description : 'Check JSON schema validity of schemas and examples (requires jv)',
)

View File

@@ -74,4 +74,14 @@ scope: {
buildPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase;
installPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase;
});
libgit2 = pkgs.libgit2.overrideAttrs (attrs: {
cmakeFlags = (attrs.cmakeFlags or [ ]) ++ [
(lib.mesonBool "EXPERIMENTAL_SHA256" true)
];
postInstall = (attrs.postInstall or "") + ''
substituteInPlace $(find $dev/include -type f) --replace-quiet '#include "git2/' '#include "git2-experimental/'
'';
});
}

View File

@@ -3,118 +3,10 @@
devFlake,
}:
let
# Some helper functions
/**
Compute a filtered closure of build inputs.
Specifically, `buildInputsClosure cond startSet` computes the closure formed
by recursive application of `p: filter cond p.buildInputs ++ filter cond p.propagatedBuildInputs`
to `startSet`.
Example:
```nix
builtInputsClosure isInternal [ pkg1 pkg2 ]
=> [ pkg1 pkg3 pkg2 pkg10 ]
```
Note: order tbd
Note: `startSet` is *NOT* filtered.
*/
buildInputsClosureCond =
cond: startSet:
let
closure = builtins.genericClosure {
startSet = map (d: {
key = d.drvPath;
value = d;
}) startSet;
operator =
d:
let
r =
map
(d': {
key = d'.drvPath;
value = d';
})
(
lib.filter cond d.value.buildInputs or [ ] ++ lib.filter cond d.value.propagatedBuildInputs or [ ]
);
in
r;
};
in
map (item: item.value) closure;
/**
`[ pkg1 pkg2 ]` -> `{ "...-pkg2.drv" = null; "...-pkg1.drv" = null }`
Note: fairly arbitrary order (hash based). Use for efficient set membership test only.
*/
byDrvPath =
l:
lib.listToAttrs (
map (c: {
name =
# Just a lookup key
builtins.unsafeDiscardStringContext c.drvPath;
value = null;
}) l
);
/**
Stable dedup.
Unlike `listToAttrs` -> `attrValues`, this preserves the input ordering,
which is more predictable ("deterministic") than e.g. sorting store paths,
whose hashes affect the ordering on every change.
*/
# TODO: add to Nixpkgs lib, refer from uniqueStrings
dedupByString =
key: l:
let
r =
lib.foldl'
(
a@{ list, set }:
elem:
let
k = builtins.unsafeDiscardStringContext (key elem);
in
if set ? ${k} then
a
else
let
# Note: O(n²) copying. Use linkedLists to concat them in one go at the end.
# https://github.com/NixOS/nixpkgs/pull/452088
newList = [ elem ] ++ list;
newSet = set // {
${k} = null;
};
in
builtins.seq newList builtins.seq newSet {
list = newList;
set = newSet;
}
)
{
list = [ ];
set = { };
}
l;
in
r.list;
in
{ pkgs }:
# TODO: don't use nix-util for this?
pkgs.nixComponents2.nix-util.overrideAttrs (
finalAttrs: prevAttrs:
attrs:
let
stdenv = pkgs.nixDependencies2.stdenv;
@@ -129,93 +21,13 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
"-D${prefix}:${rest}";
havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix;
ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags;
availableComponents = lib.filterAttrs (
k: v: lib.meta.availableOn pkgs.hostPlatform v
) allComponents;
activeComponents = buildInputsClosureCond isInternal (
lib.attrValues (finalAttrs.passthru.config.getComponents availableComponents)
);
allComponents = lib.filterAttrs (k: v: lib.isDerivation v) pkgs.nixComponents2;
internalDrvs = byDrvPath (
# Drop the attr names (not present in buildInputs anyway)
lib.attrValues availableComponents
++ lib.concatMap (c: lib.attrValues c.tests or { }) (lib.attrValues availableComponents)
);
isInternal =
dep: internalDrvs ? ${builtins.unsafeDiscardStringContext dep.drvPath or "_non-existent_"};
in
{
pname = "shell-for-nix";
passthru = {
inherit activeComponents;
# We use this attribute to store non-derivation values like functions and
# perhaps other things that are primarily for overriding and not the shell.
config = {
# Default getComponents
getComponents =
c:
builtins.removeAttrs c (
lib.optionals (!havePerl) [ "nix-perl-bindings" ]
++ lib.optionals (!buildCanExecuteHost) [ "nix-manual" ]
);
};
/**
Produce a devShell for a given set of nix components
Example:
```nix
shell.withActiveComponents (c: {
inherit (c) nix-util;
})
```
*/
withActiveComponents =
f2:
finalAttrs.finalPackage.overrideAttrs (
finalAttrs: prevAttrs: {
passthru = prevAttrs.passthru // {
config = prevAttrs.passthru.config // {
getComponents = f2;
};
};
}
);
small =
(finalAttrs.finalPackage.withActiveComponents (
c:
lib.intersectAttrs (lib.genAttrs [
"nix-cli"
"nix-util-tests"
"nix-store-tests"
"nix-expr-tests"
"nix-fetchers-tests"
"nix-flake-tests"
"nix-functional-tests"
"nix-perl-bindings"
] (_: null)) c
)).overrideAttrs
(o: {
mesonFlags = o.mesonFlags ++ [
# TODO: infer from activeComponents or vice versa
"-Dkaitai-struct-checks=false"
"-Djson-schema-checks=false"
];
});
};
pname = "shell-for-" + attrs.pname;
# Remove the version suffix to avoid unnecessary attempts to substitute in nix develop
version = lib.fileContents ../.version;
name = finalAttrs.pname;
name = attrs.pname;
installFlags = "sysconfdir=$(out)/etc";
shellHook = ''
@@ -273,8 +85,6 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
CXX_LD = "mold";
};
dontUseCmakeConfigure = true;
mesonFlags =
map (transformFlag "libutil") (ignoreCrossFile pkgs.nixComponents2.nix-util.mesonFlags)
++ map (transformFlag "libstore") (ignoreCrossFile pkgs.nixComponents2.nix-store.mesonFlags)
@@ -288,9 +98,17 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
nativeBuildInputs =
let
inputs =
dedupByString (v: "${v}") (
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.nativeBuildInputs) activeComponents)
)
attrs.nativeBuildInputs or [ ]
++ pkgs.nixComponents2.nix-util.nativeBuildInputs
++ pkgs.nixComponents2.nix-store.nativeBuildInputs
++ pkgs.nixComponents2.nix-fetchers.nativeBuildInputs
++ pkgs.nixComponents2.nix-expr.nativeBuildInputs
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.nativeBuildInputs
++ lib.optionals buildCanExecuteHost pkgs.nixComponents2.nix-manual.externalNativeBuildInputs
++ pkgs.nixComponents2.nix-internal-api-docs.nativeBuildInputs
++ pkgs.nixComponents2.nix-external-api-docs.nativeBuildInputs
++ pkgs.nixComponents2.nix-functional-tests.externalNativeBuildInputs
++ pkgs.nixComponents2.nix-json-schema-checks.externalNativeBuildInputs
++ lib.optional (
!buildCanExecuteHost
# Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479
@@ -299,7 +117,9 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
&& lib.meta.availableOn stdenv.buildPlatform (stdenv.hostPlatform.emulator pkgs.buildPackages)
) pkgs.buildPackages.mesonEmulatorHook
++ [
pkgs.buildPackages.cmake
pkgs.buildPackages.gnused
pkgs.buildPackages.changelog-d
modular.pre-commit.settings.package
(pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript)
pkgs.buildPackages.nixfmt-rfc-style
@@ -316,22 +136,18 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
# from making its way into NIX_CFLAGS_COMPILE.
lib.filter (p: !lib.hasInfix "separate-debug-info" p) inputs;
propagatedNativeBuildInputs = dedupByString (v: "${v}") (
lib.filter (x: !isInternal x) (
lib.lists.concatMap (c: c.propagatedNativeBuildInputs) activeComponents
)
);
buildInputs = [
pkgs.gbenchmark
]
++ dedupByString (v: "${v}") (
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.buildInputs) activeComponents)
)
++ attrs.buildInputs or [ ]
++ pkgs.nixComponents2.nix-util.buildInputs
++ pkgs.nixComponents2.nix-store.buildInputs
++ pkgs.nixComponents2.nix-store-tests.externalBuildInputs
++ pkgs.nixComponents2.nix-fetchers.buildInputs
++ pkgs.nixComponents2.nix-expr.buildInputs
++ pkgs.nixComponents2.nix-expr.externalPropagatedBuildInputs
++ pkgs.nixComponents2.nix-cmd.buildInputs
++ lib.optionals havePerl pkgs.nixComponents2.nix-perl-bindings.externalBuildInputs
++ lib.optional havePerl pkgs.perl;
propagatedBuildInputs = dedupByString (v: "${v}") (
lib.filter (x: !isInternal x) (lib.lists.concatMap (c: c.propagatedBuildInputs) activeComponents)
);
}
)

View File

@@ -1 +0,0 @@
../libstore-tests/data/derivation

View File

@@ -1 +0,0 @@
../../src/libutil-tests/data/memory-source-accessor

View File

@@ -20,14 +20,6 @@ schema_dir = meson.current_source_dir() / 'schema'
# Get all example files
schemas = [
{
'stem' : 'file-system-object',
'schema' : schema_dir / 'file-system-object-v1.yaml',
'files' : [
'simple.json',
'complex.json',
],
},
{
'stem' : 'hash',
'schema' : schema_dir / 'hash-v1.yaml',
@@ -71,18 +63,6 @@ schemas = [
'with-signature.json',
],
},
{
'stem' : 'derivation-options',
'schema' : schema_dir / 'derivation-options-v1.yaml',
'files' : [
'ia' / 'defaults.json',
'ia' / 'all_set.json',
'ia' / 'structuredAttrs_defaults.json',
'ia' / 'structuredAttrs_all_set.json',
'ca' / 'all_set.json',
'ca' / 'structuredAttrs_all_set.json',
],
},
]
# Derivation and Derivation output
@@ -212,19 +192,6 @@ schemas += [
},
]
# Dummy store
schemas += [
{
'stem' : 'store',
'schema' : schema_dir / 'store-v1.yaml',
'files' : [
'empty.json',
'one-flat-file.json',
'one-derivation.json',
],
},
]
# Validate each example against the schema
foreach schema : schemas
stem = schema['stem']

View File

@@ -20,7 +20,6 @@ mkMesonDerivation (finalAttrs: {
fileset = lib.fileset.unions [
../../.version
../../doc/manual/source/protocols/json/schema
../../src/libutil-tests/data/memory-source-accessor
../../src/libutil-tests/data/hash
../../src/libstore-tests/data/content-address
../../src/libstore-tests/data/store-path
@@ -30,17 +29,20 @@ mkMesonDerivation (finalAttrs: {
../../src/libstore-tests/data/path-info
../../src/libstore-tests/data/nar-info
../../src/libstore-tests/data/build-result
../../src/libstore-tests/data/dummy-store
./.
];
outputs = [ "out" ];
passthru.externalNativeBuildInputs = [
jsonschema
];
nativeBuildInputs = [
meson
ninja
jsonschema
];
]
++ finalAttrs.passthru.externalNativeBuildInputs;
doCheck = true;

View File

@@ -1 +0,0 @@
../../src/libstore-tests/data/dummy-store

View File

@@ -37,15 +37,7 @@ mkMesonDerivation (finalAttrs: {
outputs = [ "out" ];
buildInputs = [
gtest
kaitai-struct-cpp-stl-runtime
];
nativeBuildInputs = [
meson
ninja
pkg-config
passthru.externalNativeBuildInputs = [
# This can go away when we bump up to 25.11
(kaitai-struct-compiler.overrideAttrs (finalAttrs: {
version = "0.11";
@@ -56,6 +48,20 @@ mkMesonDerivation (finalAttrs: {
}))
];
passthru.externalBuildInputs = [
gtest
kaitai-struct-cpp-stl-runtime
];
buildInputs = finalAttrs.passthru.externalBuildInputs;
nativeBuildInputs = [
meson
ninja
pkg-config
]
++ finalAttrs.passthru.externalNativeBuildInputs;
doCheck = true;
mesonCheckFlags = [ "--print-errorlogs" ];
@@ -65,6 +71,6 @@ mkMesonDerivation (finalAttrs: {
'';
meta = {
platforms = lib.platforms.unix;
platforms = lib.platforms.all;
};
})

View File

@@ -2,7 +2,6 @@
#include <nlohmann/json.hpp>
#include "nix/cmd/command.hh"
#include "nix/cmd/legacy.hh"
#include "nix/cmd/markdown.hh"
#include "nix/store/store-open.hh"
#include "nix/store/local-fs-store.hh"
@@ -15,18 +14,6 @@
namespace nix {
RegisterCommand::Commands & RegisterCommand::commands()
{
static RegisterCommand::Commands commands;
return commands;
}
RegisterLegacyCommand::Commands & RegisterLegacyCommand::commands()
{
static RegisterLegacyCommand::Commands commands;
return commands;
}
nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & prefix)
{
nix::Commands res;
@@ -297,7 +284,7 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
MixDefaultProfile::MixDefaultProfile()
{
profile = getDefaultProfile().string();
profile = getDefaultProfile();
}
MixEnvironment::MixEnvironment()
@@ -391,7 +378,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
auto symlink = outLink;
if (i)
symlink += fmt("-%d", i);
store.addPermRoot(bo.path, absPath(symlink).string());
store.addPermRoot(bo.path, absPath(symlink.string()));
},
[&](const BuiltPath::Built & bfd) {
for (auto & output : bfd.outputs) {
@@ -400,7 +387,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
symlink += fmt("-%d", i);
if (output.first != "out")
symlink += fmt("-%s", output.first);
store.addPermRoot(output.second, absPath(symlink).string());
store.addPermRoot(output.second, absPath(symlink.string()));
}
},
},

View File

@@ -33,8 +33,7 @@ EvalSettings evalSettings{
// FIXME `parseFlakeRef` should take a `std::string_view`.
auto flakeRef = parseFlakeRef(fetchSettings, std::string{rest}, {}, true, false);
debug("fetching flake search path element '%s''", rest);
auto [accessor, lockedRef] =
flakeRef.resolve(fetchSettings, *state.store).lazyFetch(fetchSettings, *state.store);
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
auto storePath = nix::fetchToStore(
state.fetchSettings,
*state.store,
@@ -165,22 +164,22 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
state.parseExprFromString(
arg.expr,
compatibilitySettings.nixShellShebangArgumentsRelativeToScript
? state.rootPath(absPath(getCommandBaseDir()).string())
? state.rootPath(absPath(getCommandBaseDir()))
: state.rootPath(".")));
},
[&](const AutoArgString & arg) { v->mkString(arg.s, state.mem); },
[&](const AutoArgFile & arg) { v->mkString(readFile(arg.path.string()), state.mem); },
[&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO), state.mem); }},
[&](const AutoArgString & arg) { v->mkString(arg.s); },
[&](const AutoArgFile & arg) { v->mkString(readFile(arg.path.string())); },
[&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO)); }},
arg);
res.insert(state.symbols.create(name), v);
}
return res.finish();
}
SourcePath lookupFileArg(EvalState & state, std::string_view s, const std::filesystem::path * baseDir)
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir)
{
if (EvalSettings::isPseudoUrl(s)) {
auto accessor = fetchers::downloadTarball(*state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s));
auto accessor = fetchers::downloadTarball(state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s));
auto storePath = fetchToStore(state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy);
return state.storePath(storePath);
}
@@ -188,8 +187,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const std::files
else if (hasPrefix(s, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false);
auto [accessor, lockedRef] =
flakeRef.resolve(fetchSettings, *state.store).lazyFetch(fetchSettings, *state.store);
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
auto storePath = nix::fetchToStore(
state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
state.allowPath(storePath);
@@ -197,13 +195,12 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const std::files
}
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
// Should perhaps be a `CanonPath`?
std::string p(s.substr(1, s.size() - 2));
Path p(s.substr(1, s.size() - 2));
return state.findFile(p);
}
else
return state.rootPath(absPath(std::filesystem::path{s}, baseDir).string());
return state.rootPath(baseDir ? absPath(s, *baseDir) : absPath(s));
}
} // namespace nix

View File

@@ -134,7 +134,7 @@ struct MixFlakeOptions : virtual Args, EvalCommand
struct SourceExprCommand : virtual Args, MixFlakeOptions
{
std::optional<std::filesystem::path> file;
std::optional<Path> file;
std::optional<std::string> expr;
SourceExprCommand();
@@ -286,7 +286,11 @@ struct RegisterCommand
{
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
static Commands & commands();
static Commands & commands()
{
static Commands commands;
return commands;
}
RegisterCommand(std::vector<std::string> && name, std::function<ref<Command>()> command)
{
@@ -310,7 +314,7 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
struct MixProfile : virtual StoreCommand
{
std::optional<std::filesystem::path> profile;
std::optional<Path> profile;
MixProfile();

View File

@@ -84,6 +84,6 @@ private:
/**
* @param baseDir Optional [base directory](https://nix.dev/manual/nix/development/glossary#gloss-base-directory)
*/
SourcePath lookupFileArg(EvalState & state, std::string_view s, const std::filesystem::path * baseDir = nullptr);
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir = nullptr);
} // namespace nix

View File

@@ -17,7 +17,7 @@ class AttrCursor;
struct App
{
std::vector<DerivedPath> context;
std::filesystem::path program;
Path program;
// FIXME: add args, sandbox settings, metadata, ...
};

View File

@@ -13,7 +13,11 @@ struct RegisterLegacyCommand
{
typedef std::map<std::string, MainFunction> Commands;
static Commands & commands();
static Commands & commands()
{
static Commands commands;
return commands;
}
RegisterLegacyCommand(const std::string & name, MainFunction fun)
{

View File

@@ -19,16 +19,7 @@ struct AbstractNixRepl
typedef std::vector<std::pair<Value *, std::string>> AnnotatedValues;
/**
* Run a nix executable
*
* @todo this is a layer violation
*
* @param programName Name of the command, e.g. `nix` or `nix-env`.
* @param args aguments to the command.
*/
using RunNix =
void(const std::string & programName, const Strings & args, const std::optional<std::string> & input);
using RunNix = void(Path program, const Strings & args, const std::optional<std::string> & input);
/**
* @param runNix Function to run the nix CLI to support various

View File

@@ -132,7 +132,7 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.writeLockFile = false;
lockFlags.inputOverrides.insert_or_assign(
flake::parseInputAttrPath(inputAttrPath),
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()).string(), true));
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()), true));
}},
.completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) {
if (n == 0) {
@@ -173,7 +173,7 @@ MixFlakeOptions::MixFlakeOptions()
auto flake = flake::lockFlake(
flakeSettings,
*evalState,
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()).string()),
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir())),
{.writeLockFile = false});
for (auto & [inputName, input] : flake.lockFile.root->inputs) {
auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes
@@ -263,7 +263,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
evalSettings.pureEval = false;
auto state = getEvalState();
auto e = state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, file->string())));
auto e = state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, *file)));
Value root;
state->eval(e, root);
@@ -409,7 +409,7 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
Args::completeDir(completions, 0, prefix);
/* Look for registry entries that match the prefix. */
for (auto & registry : fetchers::getRegistries(fetchSettings, *store)) {
for (auto & registry : fetchers::getRegistries(fetchSettings, store)) {
for (auto & entry : registry->entries) {
auto from = entry.from.to_string();
if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
@@ -465,10 +465,10 @@ Installables SourceExprCommand::parseInstallables(ref<Store> store, std::vector<
state->eval(e, *vFile);
} else if (file) {
auto dir = absPath(getCommandBaseDir());
state->evalFile(lookupFileArg(*state, file->string(), &dir), *vFile);
state->evalFile(lookupFileArg(*state, *file, &dir), *vFile);
} else {
auto dir = absPath(getCommandBaseDir());
auto e = state->parseExprFromString(*expr, state->rootPath(dir.string()));
Path dir = absPath(getCommandBaseDir());
auto e = state->parseExprFromString(*expr, state->rootPath(dir));
state->eval(e, *vFile);
}
@@ -801,8 +801,7 @@ std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
std::vector<FlakeRef> res;
res.reserve(rawInstallables.size());
for (const auto & i : rawInstallables)
res.push_back(
parseFlakeRefWithFragment(fetchSettings, expandTilde(i), absPath(getCommandBaseDir()).string()).first);
res.push_back(parseFlakeRefWithFragment(fetchSettings, expandTilde(i), absPath(getCommandBaseDir())).first);
return res;
}
@@ -821,8 +820,7 @@ void RawInstallablesCommand::run(ref<Store> store)
std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
{
return {parseFlakeRefWithFragment(fetchSettings, expandTilde(_installable), absPath(getCommandBaseDir()).string())
.first};
return {parseFlakeRefWithFragment(fetchSettings, expandTilde(_installable), absPath(getCommandBaseDir())).first};
}
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)

View File

@@ -58,7 +58,8 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
{
size_t debugTraceIndex;
std::list<std::filesystem::path> loadedFiles;
// Arguments passed to :load, saved so they can be reloaded with :reload
Strings loadedFiles;
// Arguments passed to :load-flake, saved so they can be reloaded with :reload
Strings loadedFlakes;
std::function<AnnotatedValues()> getValues;
@@ -72,7 +73,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
RunNix * runNixPtr;
void runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input = {});
void runNix(Path program, const Strings & args, const std::optional<std::string> & input = {});
std::unique_ptr<ReplInteracter> interacter;
@@ -91,7 +92,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
StorePath getDerivationPath(Value & v);
ProcessLineResult processLine(std::string line);
void loadFile(const std::filesystem::path & path);
void loadFile(const Path & path);
void loadFlake(const std::string & flakeRef);
void loadFiles();
void loadFlakes();
@@ -538,9 +539,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
Value v;
evalString(arg, v);
StorePath drvPath = getDerivationPath(v);
// N.B. This need not be a local / native file path. For
// example, we might be using an SSH store to a different OS.
std::string drvPathRaw = state->store->printStorePath(drvPath);
Path drvPathRaw = state->store->printStorePath(drvPath);
if (command == ":b" || command == ":bl") {
state->store->buildPaths({
@@ -713,12 +712,12 @@ ProcessLineResult NixRepl::processLine(std::string line)
return ProcessLineResult::PromptAgain;
}
void NixRepl::loadFile(const std::filesystem::path & path)
void NixRepl::loadFile(const Path & path)
{
loadedFiles.remove(path);
loadedFiles.push_back(path);
Value v, v2;
state->evalFile(lookupFileArg(*state, path.string()), v);
state->evalFile(lookupFileArg(*state, path), v);
state->autoCallFunction(*autoArgs, v, v2);
addAttrsToScope(v2);
}
@@ -739,8 +738,8 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
}
auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true);
if (evalSettings.pureEval && !flakeRef.input.isLocked(fetchSettings))
throw Error("cannot use ':load-flake' on unlocked flake reference '%s' (use --impure to override)", flakeRefS);
if (evalSettings.pureEval && !flakeRef.input.isLocked())
throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS);
Value v;
@@ -791,7 +790,7 @@ void NixRepl::reloadFilesAndFlakes()
void NixRepl::loadFiles()
{
decltype(loadedFiles) old = loadedFiles;
Strings old = loadedFiles;
loadedFiles.clear();
for (auto & i : old) {
@@ -889,7 +888,7 @@ void NixRepl::evalString(std::string s, Value & v)
state->forceValue(v, v.determinePos(noPos));
}
void NixRepl::runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input)
void NixRepl::runNix(Path program, const Strings & args, const std::optional<std::string> & input)
{
if (runNixPtr)
(*runNixPtr)(program, args, input);

View File

@@ -69,8 +69,8 @@ nix_err nix_expr_eval_from_string(
context->last_err_code = NIX_OK;
try {
nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path)));
state->state.eval(parsedExpr, *value->value);
state->state.forceValue(*value->value, nix::noPos);
state->state.eval(parsedExpr, value->value);
state->state.forceValue(value->value, nix::noPos);
}
NIXC_CATCH_ERRS
}
@@ -80,8 +80,8 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, n
if (context)
context->last_err_code = NIX_OK;
try {
state->state.callFunction(*fn->value, *arg->value, *value->value, nix::noPos);
state->state.forceValue(*value->value, nix::noPos);
state->state.callFunction(fn->value, arg->value, value->value, nix::noPos);
state->state.forceValue(value->value, nix::noPos);
}
NIXC_CATCH_ERRS
}
@@ -91,15 +91,9 @@ nix_err nix_value_call_multi(
{
if (context)
context->last_err_code = NIX_OK;
std::vector<nix::Value *> internal_args;
internal_args.reserve(nargs);
for (size_t i = 0; i < nargs; i++)
internal_args.push_back(args[i]->value);
try {
state->state.callFunction(*fn->value, {internal_args.data(), nargs}, *value->value, nix::noPos);
state->state.forceValue(*value->value, nix::noPos);
state->state.callFunction(fn->value, {(nix::Value **) args, nargs}, value->value, nix::noPos);
state->state.forceValue(value->value, nix::noPos);
}
NIXC_CATCH_ERRS
}
@@ -109,7 +103,7 @@ nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value *
if (context)
context->last_err_code = NIX_OK;
try {
state->state.forceValue(*value->value, nix::noPos);
state->state.forceValue(value->value, nix::noPos);
}
NIXC_CATCH_ERRS
}
@@ -119,7 +113,7 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_val
if (context)
context->last_err_code = NIX_OK;
try {
state->state.forceValueDeep(*value->value);
state->state.forceValueDeep(value->value);
}
NIXC_CATCH_ERRS
}
@@ -143,8 +137,6 @@ nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Sto
void nix_eval_state_builder_free(nix_eval_state_builder * builder)
{
if (builder)
builder->~nix_eval_state_builder();
operator delete(builder, static_cast<std::align_val_t>(alignof(nix_eval_state_builder)));
}
@@ -211,8 +203,6 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c
void nix_state_free(EvalState * state)
{
if (state)
state->~EvalState();
operator delete(state, static_cast<std::align_val_t>(alignof(EvalState)));
}

View File

@@ -39,13 +39,7 @@ struct ListBuilder
struct nix_value
{
nix::Value * value;
/**
* As we move to a managed heap, we need EvalMemory in more places. Ideally, we would take in EvalState or
* EvalMemory as an argument when we need it, but we don't want to make changes to the stable C api, so we stuff it
* into the nix_value that will get passed in to the relevant functions.
*/
nix::EvalMemory * mem;
nix::Value value;
};
struct nix_string_return

View File

@@ -20,7 +20,7 @@ static const nix::Value & check_value_not_null(const nix_value * value)
if (!value) {
throw std::runtime_error("nix_value is null");
}
return *value->value;
return *((const nix::Value *) value);
}
static nix::Value & check_value_not_null(nix_value * value)
@@ -28,7 +28,7 @@ static nix::Value & check_value_not_null(nix_value * value)
if (!value) {
throw std::runtime_error("nix_value is null");
}
return *value->value;
return value->value;
}
static const nix::Value & check_value_in(const nix_value * value)
@@ -58,14 +58,9 @@ static nix::Value & check_value_out(nix_value * value)
return v;
}
static inline nix_value * new_nix_value(nix::Value * v, nix::EvalMemory & mem)
static inline nix_value * as_nix_value_ptr(nix::Value * v)
{
nix_value * ret = new (mem.allocBytes(sizeof(nix_value))) nix_value{
.value = v,
.mem = &mem,
};
nix_gc_incref(nullptr, ret);
return ret;
return reinterpret_cast<nix_value *>(v);
}
/**
@@ -74,13 +69,7 @@ static inline nix_value * new_nix_value(nix::Value * v, nix::EvalMemory & mem)
* Deals with errors and converts arguments from C++ into C types.
*/
static void nix_c_primop_wrapper(
PrimOpFun f,
void * userdata,
int arity,
nix::EvalState & state,
const nix::PosIdx pos,
nix::Value ** args,
nix::Value & v)
PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v)
{
nix_c_context ctx;
@@ -96,15 +85,8 @@ static void nix_c_primop_wrapper(
// ok because we don't see a need for this yet (e.g. inspecting thunks,
// or maybe something to make blackholes work better; we don't know).
nix::Value vTmp;
nix_value * vTmpPtr = new_nix_value(&vTmp, state.mem);
std::vector<nix_value *> external_args;
external_args.reserve(arity);
for (int i = 0; i < arity; i++) {
nix_value * external_arg = new_nix_value(args[i], state.mem);
external_args.push_back(external_arg);
}
f(userdata, &ctx, (EvalState *) &state, external_args.data(), vTmpPtr);
f(userdata, &ctx, (EvalState *) &state, (nix_value **) args, (nix_value *) &vTmp);
if (ctx.last_err_code != NIX_OK) {
/* TODO: Throw different errors depending on the error code */
@@ -153,7 +135,7 @@ PrimOp * nix_alloc_primop(
.args = {},
.arity = (size_t) arity,
.doc = doc,
.fun = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)};
.fun = std::bind(nix_c_primop_wrapper, fun, user_data, _1, _2, _3, _4)};
if (args)
for (size_t i = 0; args[i]; i++)
p->args.emplace_back(*args);
@@ -178,7 +160,8 @@ nix_value * nix_alloc_value(nix_c_context * context, EvalState * state)
if (context)
context->last_err_code = NIX_OK;
try {
nix_value * res = new_nix_value(state->state.allocValue(), state->state.mem);
nix_value * res = as_nix_value_ptr(state->state.allocValue());
nix_gc_incref(nullptr, res);
return res;
}
NIXC_CATCH_ERRS_NULL
@@ -348,10 +331,10 @@ nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value,
return nullptr;
}
auto * p = v.listView()[ix];
if (p == nullptr)
return nullptr;
state->state.forceValue(*p, nix::noPos);
return new_nix_value(p, state->state.mem);
nix_gc_incref(nullptr, p);
if (p != nullptr)
state->state.forceValue(*p, nix::noPos);
return as_nix_value_ptr(p);
}
NIXC_CATCH_ERRS_NULL
}
@@ -369,8 +352,9 @@ nix_get_list_byidx_lazy(nix_c_context * context, const nix_value * value, EvalSt
return nullptr;
}
auto * p = v.listView()[ix];
nix_gc_incref(nullptr, p);
// Note: intentionally NOT calling forceValue() to keep the element lazy
return new_nix_value(p, state->state.mem);
return as_nix_value_ptr(p);
}
NIXC_CATCH_ERRS_NULL
}
@@ -385,8 +369,9 @@ nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value
nix::Symbol s = state->state.symbols.create(name);
auto attr = v.attrs()->get(s);
if (attr) {
nix_gc_incref(nullptr, attr->value);
state->state.forceValue(*attr->value, nix::noPos);
return new_nix_value(attr->value, state->state.mem);
return as_nix_value_ptr(attr->value);
}
nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute");
return nullptr;
@@ -405,8 +390,9 @@ nix_get_attr_byname_lazy(nix_c_context * context, const nix_value * value, EvalS
nix::Symbol s = state->state.symbols.create(name);
auto attr = v.attrs()->get(s);
if (attr) {
nix_gc_incref(nullptr, attr->value);
// Note: intentionally NOT calling forceValue() to keep the attribute lazy
return new_nix_value(attr->value, state->state.mem);
return as_nix_value_ptr(attr->value);
}
nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute");
return nullptr;
@@ -454,8 +440,9 @@ nix_get_attr_byidx(nix_c_context * context, nix_value * value, EvalState * state
}
const nix::Attr & a = (*v.attrs())[i];
*name = state->state.symbols[a.name].c_str();
nix_gc_incref(nullptr, a.value);
state->state.forceValue(*a.value, nix::noPos);
return new_nix_value(a.value, state->state.mem);
return as_nix_value_ptr(a.value);
}
NIXC_CATCH_ERRS_NULL
}
@@ -474,8 +461,9 @@ nix_value * nix_get_attr_byidx_lazy(
}
const nix::Attr & a = (*v.attrs())[i];
*name = state->state.symbols[a.name].c_str();
nix_gc_incref(nullptr, a.value);
// Note: intentionally NOT calling forceValue() to keep the attribute lazy
return new_nix_value(a.value, state->state.mem);
return as_nix_value_ptr(a.value);
}
NIXC_CATCH_ERRS_NULL
}
@@ -515,7 +503,7 @@ nix_err nix_init_string(nix_c_context * context, nix_value * value, const char *
context->last_err_code = NIX_OK;
try {
auto & v = check_value_out(value);
v.mkString(std::string_view(str), *value->mem);
v.mkString(std::string_view(str));
}
NIXC_CATCH_ERRS
}
@@ -526,7 +514,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, nix_value *
context->last_err_code = NIX_OK;
try {
auto & v = check_value_out(value);
v.mkPath(s->state.rootPath(nix::CanonPath(str)), s->state.mem);
v.mkPath(s->state.rootPath(nix::CanonPath(str)));
}
NIXC_CATCH_ERRS
}

View File

@@ -104,7 +104,6 @@ MATCHER(IsAttrs, "")
MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s))
{
if (arg.type() != nString) {
*result_listener << "Expected a string got " << arg.type();
return false;
}
return arg.string_view() == s;

View File

@@ -1,6 +1,5 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/value-to-json.hh"
#include "nix/expr/static-string-data.hh"
namespace nix {
// Testing the conversion to JSON
@@ -55,7 +54,7 @@ TEST_F(JSONValueTest, IntNegative)
TEST_F(JSONValueTest, String)
{
Value v;
v.mkStringNoCopy("test"_sds);
v.mkStringNoCopy("test");
ASSERT_EQ(getJSONValue(v), "\"test\"");
}
@@ -63,7 +62,7 @@ TEST_F(JSONValueTest, StringQuotes)
{
Value v;
v.mkStringNoCopy("test\""_sds);
v.mkStringNoCopy("test\"");
ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
}
@@ -73,7 +72,7 @@ TEST_F(JSONValueTest, StringQuotes)
TEST_F(JSONValueTest, DISABLED_Path)
{
Value v;
v.mkPath(state.rootPath(CanonPath("/test")), state.mem);
v.mkPath(state.rootPath(CanonPath("/test")));
ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\"");
}
} /* namespace nix */

View File

@@ -14,7 +14,7 @@
namespace nixC {
TEST_F(nix_api_expr_test, nix_eval_state_lookup_path)
TEST_F(nix_api_store_test, nix_eval_state_lookup_path)
{
auto tmpDir = nix::createTempDir();
auto delTmpDir = std::make_unique<nix::AutoDelete>(tmpDir, true);
@@ -42,16 +42,12 @@ TEST_F(nix_api_expr_test, nix_eval_state_lookup_path)
nix_expr_eval_from_string(ctx, state, "builtins.seq <nixos-config> <nixpkgs>", ".", value);
assert_ctx_ok();
nix_state_free(state);
ASSERT_EQ(nix_get_type(ctx, value), NIX_TYPE_PATH);
assert_ctx_ok();
auto pathStr = nix_get_path_string(ctx, value);
assert_ctx_ok();
ASSERT_EQ(0, strcmp(pathStr, nixpkgs.c_str()));
nix_gc_decref(nullptr, value);
}
TEST_F(nix_api_expr_test, nix_expr_eval_from_string)

View File

@@ -661,14 +661,8 @@ INSTANTIATE_TEST_SUITE_P(
CASE(R"(null)", ""),
CASE(R"({ v = "bar"; __toString = self: self.v; })", "bar"),
CASE(R"({ v = "bar"; __toString = self: self.v; outPath = "foo"; })", "bar"),
CASE(R"({ outPath = "foo"; })", "foo")
// this is broken on cygwin because canonPath("//./test", false) returns //./test
// FIXME: don't use canonPath
#ifndef __CYGWIN__
,
CASE(R"(./test)", "/test")
#endif
));
CASE(R"({ outPath = "foo"; })", "foo"),
CASE(R"(./test)", "/test")));
#undef CASE
TEST_F(PrimOpTest, substring)

View File

@@ -1,5 +1,4 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/value.hh"
#include "nix/expr/print.hh"
@@ -36,14 +35,14 @@ TEST_F(ValuePrintingTests, tBool)
TEST_F(ValuePrintingTests, tString)
{
Value vString;
vString.mkStringNoCopy("some-string"_sds);
vString.mkStringNoCopy("some-string");
test(vString, "\"some-string\"");
}
TEST_F(ValuePrintingTests, tPath)
{
Value vPath;
vPath.mkStringNoCopy("/foo"_sds);
vPath.mkStringNoCopy("/foo");
test(vPath, "\"/foo\"");
}
@@ -268,7 +267,7 @@ struct StringPrintingTests : LibExprTest
void test(std::string_view literal, std::string_view expected, unsigned int maxLength, A... args)
{
Value v;
v.mkString(literal, state.mem);
v.mkString(literal);
std::stringstream out;
printValue(state, out, v, PrintOptions{.maxStringLength = maxLength});
@@ -290,10 +289,10 @@ TEST_F(StringPrintingTests, maxLengthTruncation)
TEST_F(ValuePrintingTests, attrsTypeFirst)
{
Value vType;
vType.mkStringNoCopy("puppy"_sds);
vType.mkStringNoCopy("puppy");
Value vApple;
vApple.mkStringNoCopy("apple"_sds);
vApple.mkStringNoCopy("apple");
BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.symbols.create("type"), &vType);
@@ -334,7 +333,7 @@ TEST_F(ValuePrintingTests, ansiColorsBool)
TEST_F(ValuePrintingTests, ansiColorsString)
{
Value v;
v.mkStringNoCopy("puppy"_sds);
v.mkStringNoCopy("puppy");
test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true});
}
@@ -342,7 +341,7 @@ TEST_F(ValuePrintingTests, ansiColorsString)
TEST_F(ValuePrintingTests, ansiColorsStringElided)
{
Value v;
v.mkStringNoCopy("puppy"_sds);
v.mkStringNoCopy("puppy");
test(
v,
@@ -353,7 +352,7 @@ TEST_F(ValuePrintingTests, ansiColorsStringElided)
TEST_F(ValuePrintingTests, ansiColorsPath)
{
Value v;
v.mkPath(state.rootPath(CanonPath("puppy")), state.mem);
v.mkPath(state.rootPath(CanonPath("puppy")));
test(v, ANSI_GREEN "/puppy" ANSI_NORMAL, PrintOptions{.ansiColors = true});
}
@@ -390,7 +389,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
TEST_F(ValuePrintingTests, ansiColorsDerivation)
{
Value vDerivation;
vDerivation.mkStringNoCopy("derivation"_sds);
vDerivation.mkStringNoCopy("derivation");
BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.s.type, &vDerivation);
@@ -413,7 +412,7 @@ TEST_F(ValuePrintingTests, ansiColorsError)
{
Value throw_ = state.getBuiltin("throw");
Value message;
message.mkStringNoCopy("uh oh!"_sds);
message.mkStringNoCopy("uh oh!");
Value vError;
vError.mkApp(&throw_, &message);
@@ -430,12 +429,12 @@ TEST_F(ValuePrintingTests, ansiColorsDerivationError)
{
Value throw_ = state.getBuiltin("throw");
Value message;
message.mkStringNoCopy("uh oh!"_sds);
message.mkStringNoCopy("uh oh!");
Value vError;
vError.mkApp(&throw_, &message);
Value vDerivation;
vDerivation.mkStringNoCopy("derivation"_sds);
vDerivation.mkStringNoCopy("derivation");
BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.s.type, &vDerivation);

View File

@@ -1,5 +1,4 @@
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/store/tests/libstore.hh"
#include <gtest/gtest.h>
@@ -28,17 +27,17 @@ TEST_F(ValueTest, staticString)
{
Value vStr1;
Value vStr2;
vStr1.mkStringNoCopy("foo"_sds);
vStr2.mkStringNoCopy("foo"_sds);
vStr1.mkStringNoCopy("foo");
vStr2.mkStringNoCopy("foo");
auto & sd1 = vStr1.string_data();
auto & sd2 = vStr2.string_data();
auto sd1 = vStr1.string_view();
auto sd2 = vStr2.string_view();
// The strings should be the same
ASSERT_EQ(sd1.view(), sd2.view());
ASSERT_EQ(sd1, sd2);
// The strings should also be backed by the same (static) allocation
ASSERT_EQ(&sd1, &sd2);
ASSERT_EQ(sd1.data(), sd2.data());
}
} // namespace nix

View File

@@ -147,7 +147,7 @@ struct AttrDb
for (auto * elem : *context) {
if (!first)
ctx.push_back(' ');
ctx.append(elem->view());
ctx.append(elem);
first = false;
}
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)

View File

@@ -60,18 +60,18 @@ EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lo
Strings EvalSettings::getDefaultNixPath()
{
Strings res;
auto add = [&](const std::filesystem::path & p, const std::string & s = std::string()) {
auto add = [&](const Path & p, const std::string & s = std::string()) {
if (std::filesystem::exists(p)) {
if (s.empty()) {
res.push_back(p.string());
res.push_back(p);
} else {
res.push_back(s + "=" + p.string());
res.push_back(s + "=" + p);
}
}
};
add(std::filesystem::path{getNixDefExpr()} / "channels");
add(rootChannelsDir() / "nixpkgs", "nixpkgs");
add(getNixDefExpr() + "/channels");
add(rootChannelsDir() + "/nixpkgs", "nixpkgs");
add(rootChannelsDir());
return res;
@@ -108,4 +108,4 @@ Path getNixDefExpr()
return settings.useXDGBaseDirectories ? getStateDir() + "/defexpr" : getHome() + "/.nix-defexpr";
}
} // namespace nix
} // namespace nix

View File

@@ -3,7 +3,6 @@
#include "nix/expr/primops.hh"
#include "nix/expr/print-options.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/value.hh"
#include "nix/util/exit.hh"
#include "nix/util/types.hh"
#include "nix/util/util.hh"
@@ -29,8 +28,6 @@
#include "parser-tab.hh"
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <cstring>
@@ -51,9 +48,6 @@ using json = nlohmann::json;
namespace nix {
/**
* Just for doc strings. Not for regular string values.
*/
static char * allocString(size_t size)
{
char * t;
@@ -67,9 +61,6 @@ static char * allocString(size_t size)
// string allocations.
// This function handles makeImmutableString(std::string_view()) by returning
// the empty string.
/**
* Just for doc strings. Not for regular string values.
*/
static const char * makeImmutableString(std::string_view s)
{
const size_t size = s.size();
@@ -81,25 +72,6 @@ static const char * makeImmutableString(std::string_view s)
return t;
}
StringData & StringData::alloc(EvalMemory & mem, size_t size)
{
void * t = mem.allocBytes(sizeof(StringData) + size + 1);
if (!t)
throw std::bad_alloc();
auto res = new (t) StringData(size);
return *res;
}
const StringData & StringData::make(EvalMemory & mem, std::string_view s)
{
if (s.empty())
return ""_sds;
auto & res = alloc(mem, s.size());
std::memcpy(&res.data_, s.data(), s.size());
res.data_[s.size()] = '\0';
return res;
}
RootValue allocRootValue(Value * v)
{
return std::allocate_shared<Value *>(traceable_allocator<Value *>(), v);
@@ -205,7 +177,7 @@ bool Value::isTrivial() const
{
return !isa<tApp, tPrimOpApp>()
&& (!isa<tThunk>()
|| (dynamic_cast<ExprAttrs *>(thunk().expr) && ((ExprAttrs *) thunk().expr)->dynamicAttrs->empty())
|| (dynamic_cast<ExprAttrs *>(thunk().expr) && ((ExprAttrs *) thunk().expr)->dynamicAttrs.empty())
|| dynamic_cast<ExprLambda *>(thunk().expr) || dynamic_cast<ExprList *>(thunk().expr));
}
@@ -517,16 +489,15 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
if (primOp.arity == 0) {
primOp.arity = 1;
auto vPrimOp = allocValue();
vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
vPrimOp->mkPrimOp(new PrimOp(primOp));
Value v;
v.mkApp(vPrimOp, vPrimOp);
auto & primOp1 = *vPrimOp->primOp();
return addConstant(
primOp1.name,
primOp.name,
v,
{
.type = nThunk, // FIXME
.doc = primOp1.doc ? primOp1.doc->c_str() : nullptr,
.doc = primOp.doc,
});
}
@@ -566,14 +537,13 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
{
if (v.isPrimOp()) {
auto v2 = &v;
auto & primOp = *v2->primOp();
if (primOp.doc)
if (auto * doc = v2->primOp()->doc)
return Doc{
.pos = {},
.name = primOp.name,
.arity = primOp.arity,
.args = primOp.args,
.doc = primOp.doc->c_str(),
.name = v2->primOp()->name,
.arity = v2->primOp()->arity,
.args = v2->primOp()->args,
.doc = doc,
};
}
if (v.isLambda()) {
@@ -615,9 +585,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
.name = name,
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
.args = {},
/* N.B. Can't use StringData here, because that would lead to an interior pointer.
NOTE: memory leak when compiled without GC. */
.doc = makeImmutableString(s.view()),
.doc = makeImmutableString(s.view()), // NOTE: memory leak when compiled without GC
};
}
if (isFunctor(v)) {
@@ -849,36 +817,35 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)
evalState.runDebugRepl(nullptr, trace.env, trace.expr);
}
void Value::mkString(std::string_view s, EvalMemory & mem)
void Value::mkString(std::string_view s)
{
mkStringNoCopy(StringData::make(mem, s));
mkStringNoCopy(makeImmutableString(s));
}
Value::StringWithContext::Context *
Value::StringWithContext::Context::fromBuilder(const NixStringContext & context, EvalMemory & mem)
Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
{
if (context.empty())
return nullptr;
auto ctx = new (mem.allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
auto ctx = new (allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
std::ranges::transform(
context, ctx->elems, [&](const NixStringContextElem & elt) { return &StringData::make(mem, elt.to_string()); });
context, ctx->elems, [](const NixStringContextElem & elt) { return makeImmutableString(elt.to_string()); });
return ctx;
}
void Value::mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem)
void Value::mkString(std::string_view s, const NixStringContext & context)
{
mkStringNoCopy(StringData::make(mem, s), Value::StringWithContext::Context::fromBuilder(context, mem));
mkStringNoCopy(makeImmutableString(s), Value::StringWithContext::Context::fromBuilder(context));
}
void Value::mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem)
void Value::mkStringMove(const char * s, const NixStringContext & context)
{
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context, mem));
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context));
}
void Value::mkPath(const SourcePath & path, EvalMemory & mem)
void Value::mkPath(const SourcePath & path)
{
mkPath(&*path.accessor, StringData::make(mem, path.path.abs()));
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
}
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
@@ -914,9 +881,9 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
}
}
ListBuilder::ListBuilder(EvalMemory & mem, size_t size)
ListBuilder::ListBuilder(size_t size)
: size(size)
, elems(size <= 2 ? inlineElems : (Value **) mem.allocBytes(size * sizeof(Value *)))
, elems(size <= 2 ? inlineElems : (Value **) allocBytes(size * sizeof(Value *)))
{
}
@@ -943,7 +910,7 @@ void EvalState::mkPos(Value & v, PosIdx p)
auto origin = positions.originOf(p);
if (auto path = std::get_if<SourcePath>(&origin)) {
auto attrs = buildBindings(3);
attrs.alloc(s.file).mkString(path->path.abs(), mem);
attrs.alloc(s.file).mkString(path->path.abs());
makePositionThunks(*this, p, attrs.alloc(s.line), attrs.alloc(s.column));
v.mkAttrs(attrs);
} else
@@ -956,8 +923,7 @@ void EvalState::mkStorePathString(const StorePath & p, Value & v)
store->printStorePath(p),
NixStringContext{
NixStringContextElem::Opaque{.path = p},
},
mem);
});
}
std::string EvalState::mkOutputStringRaw(
@@ -979,7 +945,7 @@ void EvalState::mkOutputString(
std::optional<StorePath> optStaticOutputPath,
const ExperimentalFeatureSettings & xpSettings)
{
value.mkString(mkOutputStringRaw(b, optStaticOutputPath, xpSettings), NixStringContext{b}, mem);
value.mkString(mkOutputStringRaw(b, optStaticOutputPath, xpSettings), NixStringContext{b});
}
std::string EvalState::mkSingleDerivedPathStringRaw(const SingleDerivedPath & p)
@@ -1014,8 +980,7 @@ void EvalState::mkSingleDerivedPathString(const SingleDerivedPath & p, Value & v
mkSingleDerivedPathStringRaw(p),
NixStringContext{
std::visit([](auto && v) -> NixStringContextElem { return v; }, p),
},
mem);
});
}
Value * Expr::maybeThunk(EvalState & state, Env & env)
@@ -1226,26 +1191,26 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{
auto bindings = state.buildBindings(attrs->size() + dynamicAttrs->size());
auto bindings = state.buildBindings(attrs.size() + dynamicAttrs.size());
auto dynamicEnv = &env;
bool sort = false;
if (recursive) {
/* Create a new environment that contains the attributes in
this `rec'. */
Env & env2(state.mem.allocEnv(attrs->size()));
Env & env2(state.mem.allocEnv(attrs.size()));
env2.up = &env;
dynamicEnv = &env2;
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr;
AttrDefs::iterator overrides = attrs->find(state.s.overrides);
bool hasOverrides = overrides != attrs->end();
AttrDefs::iterator overrides = attrs.find(state.s.overrides);
bool hasOverrides = overrides != attrs.end();
/* The recursive attributes are evaluated in the new
environment, while the inherited attributes are evaluated
in the original environment. */
Displacement displ = 0;
for (auto & i : *attrs) {
for (auto & i : attrs) {
Value * vAttr;
if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
vAttr = state.allocValue();
@@ -1272,8 +1237,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
"while evaluating the `__overrides` attribute");
bindings.grow(state.buildBindings(bindings.capacity() + vOverrides->attrs()->size()));
for (auto & i : *vOverrides->attrs()) {
AttrDefs::iterator j = attrs->find(i.name);
if (j != attrs->end()) {
AttrDefs::iterator j = attrs.find(i.name);
if (j != attrs.end()) {
(*bindings.bindings)[j->second.displ] = i;
env2.values[j->second.displ] = i.value;
} else
@@ -1285,13 +1250,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
else {
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
for (auto & i : *attrs)
for (auto & i : attrs)
bindings.insert(
i.first, i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), i.second.pos);
}
/* Dynamic attrs apply *after* rec and __overrides. */
for (auto & i : *dynamicAttrs) {
for (auto & i : dynamicAttrs) {
Value nameVal;
i.nameExpr->eval(state, *dynamicEnv, nameVal);
state.forceValue(nameVal, i.pos);
@@ -1325,7 +1290,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
{
/* Create a new environment that contains the attributes in this
`let'. */
Env & env2(state.mem.allocEnv(attrs->attrs->size()));
Env & env2(state.mem.allocEnv(attrs->attrs.size()));
env2.up = &env;
Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr;
@@ -1334,7 +1299,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
while the inherited attributes are evaluated in the original
environment. */
Displacement displ = 0;
for (auto & i : *attrs->attrs) {
for (auto & i : attrs->attrs) {
env2.values[displ++] = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
}
@@ -1751,9 +1716,9 @@ void ExprCall::eval(EvalState & state, Env & env, Value & v)
// 4: about 60
// 5: under 10
// This excluded attrset lambdas (`{...}:`). Contributions of mixed lambdas appears insignificant at ~150 total.
SmallValueVector<4> vArgs(args->size());
for (size_t i = 0; i < args->size(); ++i)
vArgs[i] = (*args)[i]->maybeThunk(state, env);
SmallValueVector<4> vArgs(args.size());
for (size_t i = 0; i < args.size(); ++i)
vArgs[i] = args[i]->maybeThunk(state, env);
state.callFunction(vFun, vArgs, v, pos);
}
@@ -2134,21 +2099,21 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
.atPos(pos)
.withFrame(env, *this)
.debugThrow();
std::string resultStr;
resultStr.reserve(sSize);
std::string result_str;
result_str.reserve(sSize);
for (const auto & part : strings) {
resultStr += *part;
result_str += *part;
}
v.mkPath(state.rootPath(CanonPath(resultStr)), state.mem);
v.mkPath(state.rootPath(CanonPath(result_str)));
} else {
auto & resultStr = StringData::alloc(state.mem, sSize);
auto * tmp = resultStr.data();
char * result_str = allocString(sSize + 1);
char * tmp = result_str;
for (const auto & part : strings) {
std::memcpy(tmp, part->data(), part->size());
memcpy(tmp, part->data(), part->size());
tmp += part->size();
}
*tmp = '\0';
v.mkStringMove(resultStr, context, state.mem);
*tmp = 0;
v.mkStringMove(result_str, context);
}
}
@@ -2188,8 +2153,6 @@ void EvalState::forceValueDeep(Value & v)
std::set<const Value *> seen;
[&, &state(*this)](this const auto & recurse, Value & v) {
auto _level = state.addCallDepth(v.determinePos(noPos));
if (!seen.insert(&v).second)
return;
@@ -2216,15 +2179,8 @@ void EvalState::forceValueDeep(Value & v)
}
else if (v.isList()) {
size_t index = 0;
for (auto v2 : v.listView())
try {
recurse(*v2);
index++;
} catch (Error & e) {
state.addErrorTrace(e, "while evaluating list element at index %1%", index);
throw;
}
recurse(*v2);
}
}(v);
}
@@ -2332,7 +2288,7 @@ void copyContext(const Value & v, NixStringContext & context, const Experimental
{
if (auto * ctx = v.context())
for (auto * elem : *ctx)
context.insert(NixStringContextElem::parse(elem->view(), xpSettings));
context.insert(NixStringContextElem::parse(elem, xpSettings));
}
std::string_view EvalState::forceString(
@@ -2354,7 +2310,7 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
error<EvalError>(
"the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string_view(),
(*v.context()->begin())->view())
*v.context()->begin())
.withTrace(pos, errorCtx)
.debugThrow();
}
@@ -3199,7 +3155,7 @@ std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Pat
if (EvalSettings::isPseudoUrl(value)) {
try {
auto accessor = fetchers::downloadTarball(*store, fetchSettings, EvalSettings::resolvePseudoUrl(value));
auto accessor = fetchers::downloadTarball(store, fetchSettings, EvalSettings::resolvePseudoUrl(value));
auto storePath = fetchToStore(fetchSettings, *store, SourcePath(accessor), FetchMode::Copy);
return finish(this->storePath(storePath));
} catch (Error & e) {

View File

@@ -12,7 +12,7 @@ namespace nix {
* Note: Various places expect the allocated memory to be zeroed.
*/
[[gnu::always_inline]]
inline void * EvalMemory::allocBytes(size_t n)
inline void * allocBytes(size_t n)
{
void * p;
#if NIX_USE_BOEHMGC

View File

@@ -108,7 +108,7 @@ struct PrimOp
/**
* Optional free-form documentation about the primop.
*/
std::optional<std::string> doc;
const char * doc = nullptr;
/**
* Add a trace item, while calling the `<name>` builtin.
@@ -335,7 +335,6 @@ public:
EvalMemory & operator=(const EvalMemory &) = delete;
EvalMemory & operator=(EvalMemory &&) = delete;
inline void * allocBytes(size_t n);
inline Value * allocValue();
inline Env & allocEnv(size_t size);
@@ -349,7 +348,7 @@ public:
ListBuilder buildList(size_t size)
{
stats.nrListElems += size;
return ListBuilder(*this, size);
return ListBuilder(size);
}
const Statistics & getStats() const &

View File

@@ -31,7 +31,6 @@ headers = [ config_pub_h ] + files(
'print.hh',
'repl-exit-status.hh',
'search-path.hh',
'static-string-data.hh',
'symbol-table.hh',
'value-to-json.hh',
'value-to-xml.hh',

View File

@@ -3,7 +3,6 @@
#include <map>
#include <span>
#include <memory>
#include <vector>
#include <memory_resource>
#include <algorithm>
@@ -12,7 +11,6 @@
#include "nix/expr/value.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/eval-error.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/util/pos-idx.hh"
#include "nix/expr/counter.hh"
#include "nix/util/pos-table.hh"
@@ -188,18 +186,22 @@ struct ExprString : Expr
* This is only for strings already allocated in our polymorphic allocator,
* or that live at least that long (e.g. c++ string literals)
*/
ExprString(const StringData & s)
ExprString(const char * s)
{
v.mkStringNoCopy(s);
};
ExprString(std::pmr::polymorphic_allocator<char> & alloc, std::string_view sv)
{
if (sv.size() == 0) {
v.mkStringNoCopy(""_sds);
auto len = sv.length();
if (len == 0) {
v.mkStringNoCopy("");
return;
}
v.mkStringNoCopy(StringData::make(*alloc.resource(), sv));
char * s = alloc.allocate(len + 1);
sv.copy(s, len);
s[len] = '\0';
v.mkStringNoCopy(s);
};
Value * maybeThunk(EvalState & state, Env & env) override;
@@ -214,7 +216,11 @@ struct ExprPath : Expr
ExprPath(std::pmr::polymorphic_allocator<char> & alloc, ref<SourceAccessor> accessor, std::string_view sv)
: accessor(accessor)
{
v.mkPath(&*accessor, StringData::make(*alloc.resource(), sv));
auto len = sv.length();
char * s = alloc.allocate(len + 1);
sv.copy(s, len);
s[len] = '\0';
v.mkPath(&*accessor, s);
}
Value * maybeThunk(EvalState & state, Env & env) override;
@@ -339,7 +345,7 @@ struct ExprOpHasAttr : Expr
Expr * e;
std::span<AttrName> attrPath;
ExprOpHasAttr(std::pmr::polymorphic_allocator<char> & alloc, Expr * e, std::span<AttrName> attrPath)
ExprOpHasAttr(std::pmr::polymorphic_allocator<char> & alloc, Expr * e, std::vector<AttrName> attrPath)
: e(e)
, attrPath({alloc.allocate_object<AttrName>(attrPath.size()), attrPath.size()})
{
@@ -395,13 +401,9 @@ struct ExprAttrs : Expr
}
};
typedef std::pmr::map<Symbol, AttrDef> AttrDefs;
/**
* attrs will never be null. we use std::optional so that we can call emplace() to re-initialize the value with a
* new pmr::map using a different allocator (move assignment will copy into the old allocator)
*/
std::optional<AttrDefs> attrs;
std::unique_ptr<std::pmr::vector<Expr *>> inheritFromExprs;
typedef std::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs;
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
struct DynamicAttrDef
{
@@ -413,20 +415,13 @@ struct ExprAttrs : Expr
, pos(pos) {};
};
typedef std::pmr::vector<DynamicAttrDef> DynamicAttrDefs;
/**
* dynamicAttrs will never be null. See comment on AttrDefs above.
*/
std::optional<DynamicAttrDefs> dynamicAttrs;
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs;
ExprAttrs(const PosIdx & pos)
: recursive(false)
, pos(pos)
, attrs(AttrDefs{})
, dynamicAttrs(DynamicAttrDefs{}) {};
, pos(pos) {};
ExprAttrs()
: recursive(false)
, attrs(AttrDefs{})
, dynamicAttrs(DynamicAttrDefs{}) {};
: recursive(false) {};
PosIdx getPos() const override
{
@@ -438,14 +433,13 @@ struct ExprAttrs : Expr
std::shared_ptr<const StaticEnv> bindInheritSources(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
Env * buildInheritFromEnv(EvalState & state, Env & up);
void showBindings(const SymbolTable & symbols, std::ostream & str) const;
void moveDataToAllocator(std::pmr::polymorphic_allocator<char> & alloc);
};
struct ExprList : Expr
{
std::span<Expr *> elems;
ExprList(std::pmr::polymorphic_allocator<char> & alloc, std::span<Expr *> exprs)
ExprList(std::pmr::polymorphic_allocator<char> & alloc, std::vector<Expr *> exprs)
: elems({alloc.allocate_object<Expr *>(exprs.size()), exprs.size()})
{
std::ranges::copy(exprs, elems.begin());
@@ -574,7 +568,7 @@ public:
const PosTable & positions,
std::pmr::polymorphic_allocator<char> & alloc,
PosIdx pos,
const FormalsBuilder & formals,
FormalsBuilder formals,
Expr * body)
: ExprLambda(positions, alloc, pos, Symbol(), formals, body) {};
@@ -593,14 +587,11 @@ public:
struct ExprCall : Expr
{
Expr * fun;
/**
* args will never be null. See comment on ExprAttrs::AttrDefs below.
*/
std::optional<std::pmr::vector<Expr *>> args;
std::vector<Expr *> args;
PosIdx pos;
std::optional<PosIdx> cursedOrEndPos; // used during parsing to warn about https://github.com/NixOS/nix/issues/11118
ExprCall(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args)
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
: fun(fun)
, args(args)
, pos(pos)
@@ -608,7 +599,7 @@ struct ExprCall : Expr
{
}
ExprCall(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args, PosIdx && cursedOrEndPos)
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos)
: fun(fun)
, args(args)
, pos(pos)
@@ -623,7 +614,6 @@ struct ExprCall : Expr
virtual void resetCursedOr() override;
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) override;
void moveDataToAllocator(std::pmr::polymorphic_allocator<char> & alloc);
COMMON_METHODS
};
@@ -769,19 +759,7 @@ struct ExprConcatStrings : Expr
std::pmr::polymorphic_allocator<char> & alloc,
const PosIdx & pos,
bool forceString,
std::span<std::pair<PosIdx, Expr *>> es)
: pos(pos)
, forceString(forceString)
, es({alloc.allocate_object<std::pair<PosIdx, Expr *>>(es.size()), es.size()})
{
std::ranges::copy(es, this->es.begin());
};
ExprConcatStrings(
std::pmr::polymorphic_allocator<char> & alloc,
const PosIdx & pos,
bool forceString,
std::initializer_list<std::pair<PosIdx, Expr *>> es)
const std::vector<std::pair<PosIdx, Expr *>> & es)
: pos(pos)
, forceString(forceString)
, es({alloc.allocate_object<std::pair<PosIdx, Expr *>>(es.size()), es.size()})
@@ -841,7 +819,7 @@ public:
// we define some calls to add explicitly so that the argument can be passed in as initializer lists
template<class C>
[[gnu::always_inline]]
C * add(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args)
C * add(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
requires(std::same_as<C, ExprCall>)
{
return alloc.new_object<C>(pos, fun, std::move(args));
@@ -849,7 +827,7 @@ public:
template<class C>
[[gnu::always_inline]]
C * add(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args, PosIdx && cursedOrEndPos)
C * add(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos)
requires(std::same_as<C, ExprCall>)
{
return alloc.new_object<C>(pos, fun, std::move(args), std::move(cursedOrEndPos));
@@ -861,19 +839,7 @@ public:
add(std::pmr::polymorphic_allocator<char> & alloc,
const PosIdx & pos,
bool forceString,
std::span<std::pair<PosIdx, Expr *>> es)
requires(std::same_as<C, ExprConcatStrings>)
{
return alloc.new_object<C>(alloc, pos, forceString, es);
}
template<class C>
[[gnu::always_inline]]
C *
add(std::pmr::polymorphic_allocator<char> & alloc,
const PosIdx & pos,
bool forceString,
std::initializer_list<std::pair<PosIdx, Expr *>> es)
const std::vector<std::pair<PosIdx, Expr *>> & es)
requires(std::same_as<C, ExprConcatStrings>)
{
return alloc.new_object<C>(alloc, pos, forceString, es);

View File

@@ -4,8 +4,6 @@
#include <limits>
#include "nix/expr/eval.hh"
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
namespace nix {
@@ -47,79 +45,6 @@ struct ParserLocation
}
};
/**
* This represents a string-like parse that possibly has yet to be constructed.
*
* Examples:
* "foo"
* ${"foo" + "bar"}
* "foo.bar"
* "foo-${a}"
*
* Using this type allows us to avoid construction altogether in cases where what we actually need is the string
* contents. For example in foo."bar.baz", there is no need to construct an AST node for "bar.baz", but we don't know
* that until we bubble the value up during parsing and see that it's a node in an AttrPath.
*/
class ToBeStringyExpr
{
private:
using Raw = std::variant<std::monostate, std::string_view, Expr *>;
Raw raw;
public:
ToBeStringyExpr() = default;
ToBeStringyExpr(std::string_view v)
: raw(v)
{
}
ToBeStringyExpr(Expr * expr)
: raw(expr)
{
assert(expr);
}
/**
* Visits the expression and invokes an overloaded functor object \ref f.
* If the underlying Expr has a dynamic type of ExprString the overload taking std::string_view
* is invoked.
*
* Used to consistently handle simple StringExpr ${"string"} as non-dynamic attributes.
* @see https://github.com/NixOS/nix/issues/14642
*/
template<class F>
void visit(F && f)
{
std::visit(
overloaded{
[&](std::string_view str) { f(str); },
[&](Expr * expr) {
ExprString * str = dynamic_cast<ExprString *>(expr);
if (str)
f(str->v.string_view());
else
f(expr);
},
[](std::monostate) { unreachable(); }},
raw);
}
/**
* Get or create an Expr from either an existing Expr or from a string.
* Delays the allocation or an AST node in case the parser only cares about string contents.
*/
Expr * toExpr(Exprs & exprs)
{
return std::visit(
overloaded{
[&](std::string_view str) -> Expr * { return exprs.add<ExprString>(exprs.alloc, str); },
[&](Expr * expr) { return expr; },
[](std::monostate) -> Expr * { unreachable(); }},
raw);
}
};
struct LexerState
{
/**
@@ -169,7 +94,7 @@ struct ParserState
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
void validateFormals(FormalsBuilder & formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std::variant<Expr *, StringToken>>> es);
Expr * stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
PosIdx at(const ParserLocation & loc);
};
@@ -199,8 +124,8 @@ inline void ParserState::addAttr(
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
ExprAttrs * nested;
if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(i->symbol);
if (j != attrs->attrs->end()) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
nested = dynamic_cast<ExprAttrs *>(j->second.e);
if (!nested) {
attrPath.erase(i + 1, attrPath.end());
@@ -208,11 +133,11 @@ inline void ParserState::addAttr(
}
} else {
nested = exprs.add<ExprAttrs>();
(*attrs->attrs)[i->symbol] = ExprAttrs::AttrDef(nested, pos);
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
}
} else {
nested = exprs.add<ExprAttrs>();
attrs->dynamicAttrs->push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
}
attrs = nested;
}
@@ -221,7 +146,7 @@ inline void ParserState::addAttr(
if (i->symbol) {
addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos));
} else {
attrs->dynamicAttrs->push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
}
auto it = lexerState.positionToDocComment.find(pos);
@@ -238,8 +163,8 @@ inline void ParserState::addAttr(
inline void
ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
{
ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(symbol);
if (j != attrs->attrs->end()) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol);
if (j != attrs->attrs.end()) {
// This attr path is already defined. However, if both
// e and the expr pointed by the attr path are two attribute sets,
// we want to merge them.
@@ -254,8 +179,8 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
// See https://github.com/NixOS/nix/issues/9020.
if (jAttrs && ae) {
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
jAttrs->inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>();
for (auto & ad : *ae->attrs) {
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
for (auto & ad : ae->attrs) {
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
@@ -265,12 +190,12 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
addAttr(jAttrs, attrPath, ad.first, std::move(ad.second));
attrPath.pop_back();
}
ae->attrs->clear();
jAttrs->dynamicAttrs->insert(
jAttrs->dynamicAttrs->end(),
std::make_move_iterator(ae->dynamicAttrs->begin()),
std::make_move_iterator(ae->dynamicAttrs->end()));
ae->dynamicAttrs->clear();
ae->attrs.clear();
jAttrs->dynamicAttrs.insert(
jAttrs->dynamicAttrs.end(),
std::make_move_iterator(ae->dynamicAttrs.begin()),
std::make_move_iterator(ae->dynamicAttrs.end()));
ae->dynamicAttrs.clear();
if (ae->inheritFromExprs) {
jAttrs->inheritFromExprs->insert(
jAttrs->inheritFromExprs->end(),
@@ -283,7 +208,7 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb
}
} else {
// This attr path is not defined. Let's create it.
attrs->attrs->emplace(symbol, def);
attrs->attrs.emplace(symbol, def);
def.e->setName(symbol);
}
}
@@ -312,10 +237,10 @@ inline void ParserState::validateFormals(FormalsBuilder & formals, PosIdx pos, S
}
inline Expr *
ParserState::stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std::variant<Expr *, StringToken>>> es)
ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
{
if (es.empty())
return exprs.add<ExprString>(""_sds);
return exprs.add<ExprString>("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@@ -407,7 +332,7 @@ ParserState::stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std:
// If there is nothing at all, return the empty string directly.
// This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
if (es2.size() == 0) {
auto * const result = exprs.add<ExprString>(""_sds);
auto * const result = exprs.add<ExprString>("");
return result;
}
@@ -416,7 +341,7 @@ ParserState::stripIndentation(const PosIdx pos, std::span<std::pair<PosIdx, std:
auto * const result = (es2)[0].second;
return result;
}
return exprs.add<ExprConcatStrings>(exprs.alloc, pos, true, es2);
return exprs.add<ExprConcatStrings>(exprs.alloc, pos, true, std::move(es2));
}
inline PosIdx LexerState::at(const ParserLocation & loc)

View File

@@ -12,7 +12,11 @@ struct RegisterPrimOp
{
typedef std::vector<PrimOp> PrimOps;
static PrimOps & primOps();
static PrimOps & primOps()
{
static PrimOps primOps;
return primOps;
}
/**
* You can register a constant by passing an arity of 0. fun

View File

@@ -110,7 +110,7 @@ struct PrintOptions
* `PrintOptions` for unknown and therefore potentially large values in error messages,
* to avoid printing "too much" output.
*/
static constexpr PrintOptions errorPrintOptions = PrintOptions{
static PrintOptions errorPrintOptions = PrintOptions{
.ansiColors = true,
.maxDepth = 10,
.maxAttrs = 10,

View File

@@ -1,44 +0,0 @@
#pragma once
///@file
#include "nix/expr/value.hh"
namespace nix {
template<size_t N>
struct StringData::Static
{
/**
* @note Must be first to make layout compatible with StringData.
*/
const size_t size = N - 1;
char data[N];
consteval Static(const char (&str)[N])
{
static_assert(N > 0);
if (str[size] != '\0')
throw;
std::copy_n(str, N, data);
}
operator const StringData &() const &
{
static_assert(sizeof(decltype(*this)) >= sizeof(StringData));
static_assert(alignof(decltype(*this)) == alignof(StringData));
/* NOTE: This cast is somewhat on the fence of what's legal in C++.
The question boils down to whether flexible array members are
layout compatible with fixed-size arrays. This is a gray area, since
FAMs are not standard anyway.
*/
return *reinterpret_cast<const StringData *>(this);
}
};
template<StringData::Static S>
const StringData & operator""_sds()
{
return S;
}
} // namespace nix

View File

@@ -3,7 +3,6 @@
#include <memory_resource>
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/util/chunked-vector.hh"
#include "nix/util/error.hh"
@@ -17,6 +16,7 @@ class SymbolValue : protected Value
friend class SymbolStr;
friend class SymbolTable;
uint32_t size_;
uint32_t idx;
SymbolValue() = default;
@@ -24,7 +24,7 @@ class SymbolValue : protected Value
public:
operator std::string_view() const noexcept
{
return string_view();
return {c_str(), size_};
}
};
@@ -96,13 +96,13 @@ class SymbolStr
SymbolValueStore & store;
std::string_view s;
std::size_t hash;
std::pmr::memory_resource & resource;
std::pmr::polymorphic_allocator<char> & alloc;
Key(SymbolValueStore & store, std::string_view s, std::pmr::memory_resource & stringMemory)
Key(SymbolValueStore & store, std::string_view s, std::pmr::polymorphic_allocator<char> & stringAlloc)
: store(store)
, s(s)
, hash(HashType{}(s))
, resource(stringMemory)
, alloc(stringAlloc)
{
}
};
@@ -122,10 +122,14 @@ public:
// for multi-threaded implementations: lock store and allocator here
const auto & [v, idx] = key.store.add(SymbolValue{});
if (size == 0) {
v.mkStringNoCopy(""_sds, nullptr);
v.mkStringNoCopy("", nullptr);
} else {
v.mkStringNoCopy(StringData::make(key.resource, key.s));
auto s = key.alloc.allocate(size + 1);
memcpy(s, key.s.data(), size);
s[size] = '\0';
v.mkStringNoCopy(s, nullptr);
}
v.size_ = size;
v.idx = idx;
this->s = &v;
}
@@ -135,12 +139,6 @@ public:
return *s == s2;
}
[[gnu::always_inline]]
const StringData & string_data() const noexcept
{
return s->string_data();
}
[[gnu::always_inline]]
const char * c_str() const noexcept
{
@@ -157,17 +155,13 @@ public:
[[gnu::always_inline]]
bool empty() const noexcept
{
auto * p = &s->string_data();
// Save a dereference in the sentinel value case
if (p == &""_sds)
return true;
return p->size() == 0;
return s->size_ == 0;
}
[[gnu::always_inline]]
size_t size() const noexcept
{
return s->string_data().size();
return s->size_;
}
[[gnu::always_inline]]
@@ -265,6 +259,7 @@ private:
* During its lifetime the monotonic buffer holds all strings and nodes, if the symbol set is node based.
*/
std::pmr::monotonic_buffer_resource buffer;
std::pmr::polymorphic_allocator<char> stringAlloc{&buffer};
SymbolStr::SymbolValueStore store{16};
/**
@@ -287,7 +282,7 @@ public:
// Most symbols are looked up more than once, so we trade off insertion performance
// for lookup performance.
// FIXME: make this thread-safe.
return Symbol(*symbols.insert(SymbolStr::Key{store, s, buffer}).first);
return Symbol(*symbols.insert(SymbolStr::Key{store, s, stringAlloc}).first);
}
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const

View File

@@ -1,14 +1,8 @@
#pragma once
///@file
#include <bit>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <memory>
#include <memory_resource>
#include <span>
#include <string_view>
#include <type_traits>
#include <concepts>
@@ -88,7 +82,6 @@ class PosIdx;
struct Pos;
class StorePath;
class EvalState;
class EvalMemory;
class XMLWriter;
class Printer;
@@ -162,7 +155,7 @@ class ListBuilder
Value * inlineElems[2] = {nullptr, nullptr};
public:
Value ** elems;
ListBuilder(EvalMemory & mem, size_t size);
ListBuilder(size_t size);
// NOTE: Can be noexcept because we are just copying integral values and
// raw pointers.
@@ -193,91 +186,6 @@ public:
friend struct Value;
};
class StringData
{
public:
using size_type = std::size_t;
size_type size_;
char data_[];
/*
* This in particular ensures that we cannot have a `StringData`
* that we use by value, which is just what we want!
*
* Dynamically sized types aren't a thing in C++ and even flexible array
* members are a language extension and beyond the realm of standard C++.
* Technically, sizeof data_ member is 0 and the intended way to use flexible
* array members is to allocate sizeof(StrindData) + count * sizeof(char) bytes
* and the compiler will consider alignment restrictions for the FAM.
*
*/
StringData(StringData &&) = delete;
StringData & operator=(StringData &&) = delete;
StringData(const StringData &) = delete;
StringData & operator=(const StringData &) = delete;
~StringData() = default;
private:
StringData() = delete;
explicit StringData(size_type size)
: size_(size)
{
}
public:
/**
* Allocate StringData on the (possibly) GC-managed heap and copy
* the contents of s to it.
*/
static const StringData & make(EvalMemory & mem, std::string_view s);
/**
* Allocate StringData on the (possibly) GC-managed heap.
* @param size Length of the string (without the NUL terminator).
*/
static StringData & alloc(EvalMemory & mem, size_t size);
size_t size() const
{
return size_;
}
char * data() noexcept
{
return data_;
}
const char * data() const noexcept
{
return data_;
}
const char * c_str() const noexcept
{
return data_;
}
constexpr std::string_view view() const noexcept
{
return std::string_view(data_, size_);
}
template<size_t N>
struct Static;
static StringData & make(std::pmr::memory_resource & resource, std::string_view s)
{
auto & res =
*new (resource.allocate(sizeof(StringData) + s.size() + 1, alignof(StringData))) StringData(s.size());
std::memcpy(res.data_, s.data(), s.size());
res.data_[s.size()] = '\0';
return res;
}
};
namespace detail {
/**
@@ -311,7 +219,7 @@ struct ValueBase
*/
struct StringWithContext
{
const StringData * str;
const char * c_str;
/**
* The type of the context itself.
@@ -326,7 +234,7 @@ struct ValueBase
*/
struct Context
{
using value_type = const StringData *;
using value_type = const char *;
using size_type = std::size_t;
using iterator = const value_type *;
@@ -365,7 +273,7 @@ struct ValueBase
/**
* @return null pointer when context.empty()
*/
static Context * fromBuilder(const NixStringContext & context, EvalMemory & mem);
static Context * fromBuilder(const NixStringContext & context);
};
/**
@@ -377,7 +285,7 @@ struct ValueBase
struct Path
{
SourceAccessor * accessor;
const StringData * path;
const char * path;
};
struct Null
@@ -738,13 +646,13 @@ protected:
void getStorage(StringWithContext & string) const noexcept
{
string.context = untagPointer<decltype(string.context)>(payload[0]);
string.str = std::bit_cast<const StringData *>(payload[1]);
string.c_str = std::bit_cast<const char *>(payload[1]);
}
void getStorage(Path & path) const noexcept
{
path.accessor = untagPointer<decltype(path.accessor)>(payload[0]);
path.path = std::bit_cast<const StringData *>(payload[1]);
path.path = std::bit_cast<const char *>(payload[1]);
}
void setStorage(NixInt integer) noexcept
@@ -789,7 +697,7 @@ protected:
void setStorage(StringWithContext string) noexcept
{
setUntaggablePayload<pdString>(string.context, string.str);
setUntaggablePayload<pdString>(string.context, string.c_str);
}
void setStorage(Path path) noexcept
@@ -1142,22 +1050,22 @@ public:
setStorage(b);
}
void mkStringNoCopy(const StringData & s, const Value::StringWithContext::Context * context = nullptr) noexcept
void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept
{
setStorage(StringWithContext{.str = &s, .context = context});
setStorage(StringWithContext{.c_str = s, .context = context});
}
void mkString(std::string_view s, EvalMemory & mem);
void mkString(std::string_view s);
void mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem);
void mkString(std::string_view s, const NixStringContext & context);
void mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem);
void mkStringMove(const char * s, const NixStringContext & context);
void mkPath(const SourcePath & path, EvalMemory & mem);
void mkPath(const SourcePath & path);
inline void mkPath(SourceAccessor * accessor, const StringData & path) noexcept
inline void mkPath(SourceAccessor * accessor, const char * path) noexcept
{
setStorage(Path{.accessor = accessor, .path = &path});
setStorage(Path{.accessor = accessor, .path = path});
}
inline void mkNull() noexcept
@@ -1255,23 +1163,17 @@ public:
SourcePath path() const
{
return SourcePath(
ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), std::string(pathStrView())));
}
const StringData & string_data() const noexcept
{
return *getStorage<StringWithContext>().str;
}
const char * c_str() const noexcept
{
return getStorage<StringWithContext>().str->data();
return SourcePath(ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), pathStr()));
}
std::string_view string_view() const noexcept
{
return string_data().view();
return std::string_view{getStorage<StringWithContext>().c_str};
}
const char * c_str() const noexcept
{
return getStorage<StringWithContext>().c_str;
}
const Value::StringWithContext::Context * context() const noexcept
@@ -1331,12 +1233,12 @@ public:
const char * pathStr() const noexcept
{
return getStorage<Path>().path->c_str();
return getStorage<Path>().path;
}
std::string_view pathStrView() const noexcept
{
return getStorage<Path>().path->view();
return std::string_view{getStorage<Path>().path};
}
SourceAccessor * pathAccessor() const noexcept

View File

@@ -151,7 +151,7 @@ public:
bool string(string_t & val) override
{
forceNoNullByte(val);
rs->value(state).mkString(val, state.mem);
rs->value(state).mkString(val);
rs->add();
return true;
}

View File

@@ -74,9 +74,9 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
{
typedef const AttrDefs::value_type * Attr;
typedef const decltype(attrs)::value_type * Attr;
std::vector<Attr> sorted;
for (auto & i : *attrs)
for (auto & i : attrs)
sorted.push_back(&i);
std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
std::string_view sa = symbols[a->first], sb = symbols[b->first];
@@ -122,7 +122,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
str << "; ";
}
}
for (auto & i : *dynamicAttrs) {
for (auto & i : dynamicAttrs) {
str << "\"${";
i.nameExpr->show(symbols, str);
str << "}\" = ";
@@ -191,7 +191,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
{
str << '(';
fun->show(symbols, str);
for (auto e : *args) {
for (auto e : args) {
str << ' ';
e->show(symbols, str);
}
@@ -399,29 +399,17 @@ ExprAttrs::bindInheritSources(EvalState & es, const std::shared_ptr<const Static
return inner;
}
void ExprAttrs::moveDataToAllocator(std::pmr::polymorphic_allocator<char> & alloc)
{
AttrDefs newAttrs{std::move(*attrs), alloc};
attrs.emplace(std::move(newAttrs), alloc);
DynamicAttrDefs newDynamicAttrs{std::move(*dynamicAttrs), alloc};
dynamicAttrs.emplace(std::move(newDynamicAttrs), alloc);
if (inheritFromExprs)
inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>(std::move(*inheritFromExprs), alloc);
}
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
moveDataToAllocator(es.mem.exprs.alloc);
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
if (recursive) {
auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->size());
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs.size());
Displacement displ = 0;
for (auto & i : *attrs)
for (auto & i : attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
return newEnv;
}();
@@ -429,20 +417,20 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
// No need to sort newEnv since attrs is in sorted order.
auto inheritFromEnv = bindInheritSources(es, newEnv);
for (auto & i : *attrs)
for (auto & i : attrs)
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
for (auto & i : *dynamicAttrs) {
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(es, newEnv);
i.valueExpr->bindVars(es, newEnv);
}
} else {
auto inheritFromEnv = bindInheritSources(es, env);
for (auto & i : *attrs)
for (auto & i : attrs)
i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv));
for (auto & i : *dynamicAttrs) {
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(es, env);
i.valueExpr->bindVars(es, env);
}
@@ -485,31 +473,23 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
body->bindVars(es, newEnv);
}
void ExprCall::moveDataToAllocator(std::pmr::polymorphic_allocator<char> & alloc)
{
std::pmr::vector<Expr *> newArgs{std::move(*args), alloc};
args.emplace(std::move(newArgs), alloc);
}
void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
moveDataToAllocator(es.mem.exprs.alloc);
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
fun->bindVars(es, env);
for (auto e : *args)
for (auto e : args)
e->bindVars(es, env);
}
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
attrs->moveDataToAllocator(es.mem.exprs.alloc);
auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs->size());
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs.size());
Displacement displ = 0;
for (auto & i : *attrs->attrs)
for (auto & i : attrs->attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
return newEnv;
}();
@@ -517,7 +497,7 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
// No need to sort newEnv since attrs->attrs is in sorted order.
auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
for (auto & i : *attrs->attrs)
for (auto & i : attrs->attrs)
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
if (es.debugRepl)

View File

@@ -70,6 +70,11 @@ mkMesonLibrary (finalAttrs: {
nix-util
nix-store
nix-fetchers
]
++ finalAttrs.passthru.externalPropagatedBuildInputs;
# Hack for sake of the dev shell
passthru.externalPropagatedBuildInputs = [
boost
nlohmann_json
]

View File

@@ -115,7 +115,7 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P
static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
if (auto e2 = dynamic_cast<ExprCall *>(fn)) {
e2->args->push_back(arg);
e2->args.push_back(arg);
return fn;
}
return exprs.add<ExprCall>(pos, fn, {arg});
@@ -126,26 +126,26 @@ static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
%define api.value.type variant
%type <Expr *> start expr expr_function expr_if expr_op
%type <Expr *> expr_select expr_simple expr_app
%type <Expr *> expr_pipe_from expr_pipe_into
%type <std::pmr::vector<Expr *>> list
%type <ExprAttrs *> binds binds1
%type <FormalsBuilder> formals formal_set
%type <Formal> formal
%type <std::vector<AttrName>> attrpath
%type <std::vector<std::pair<AttrName, PosIdx>>> attrs
%type <std::vector<std::pair<PosIdx, Expr *>>> string_parts_interpolated
%type <std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>>> ind_string_parts
%type <Expr *> path_start
%type <ToBeStringyExpr> string_parts string_attr
%type <StringToken> attr
%token <StringToken> ID
%token <StringToken> STR IND_STR
%token <NixInt> INT_LIT
%token <NixFloat> FLOAT_LIT
%token <StringToken> PATH HPATH SPATH PATH_END
%token <StringToken> URI
%type <nix::Expr *> start expr expr_function expr_if expr_op
%type <nix::Expr *> expr_select expr_simple expr_app
%type <nix::Expr *> expr_pipe_from expr_pipe_into
%type <std::vector<Expr *>> list
%type <nix::ExprAttrs *> binds binds1
%type <nix::FormalsBuilder> formals formal_set
%type <nix::Formal> formal
%type <std::vector<nix::AttrName>> attrpath
%type <std::vector<std::pair<nix::AttrName, nix::PosIdx>>> attrs
%type <std::vector<std::pair<nix::PosIdx, nix::Expr *>>> string_parts_interpolated
%type <std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>>> ind_string_parts
%type <nix::Expr *> path_start
%type <std::variant<nix::Expr *, std::string_view>> string_parts string_attr
%type <nix::StringToken> attr
%token <nix::StringToken> ID
%token <nix::StringToken> STR IND_STR
%token <nix::NixInt> INT_LIT
%token <nix::NixFloat> FLOAT_LIT
%token <nix::StringToken> PATH HPATH SPATH PATH_END
%token <nix::StringToken> URI
%token IF THEN ELSE ASSERT WITH LET IN_KW REC INHERIT EQ NEQ AND OR IMPL OR_KW
%token PIPE_FROM PIPE_INTO /* <| and |> */
%token DOLLAR_CURLY /* == ${ */
@@ -186,7 +186,7 @@ expr_function
| formal_set ':' expr_function[body]
{
state->validateFormals($formal_set);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, $formal_set, $body);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, std::move($formal_set), $body);
$$ = me;
SET_DOC_POS(me, @1);
}
@@ -194,7 +194,7 @@ expr_function
{
auto arg = state->symbols.create($ID);
state->validateFormals($formal_set, CUR_POS, arg);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, $formal_set, $body);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, std::move($formal_set), $body);
$$ = me;
SET_DOC_POS(me, @1);
}
@@ -202,7 +202,7 @@ expr_function
{
auto arg = state->symbols.create($ID);
state->validateFormals($formal_set, CUR_POS, arg);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, $formal_set, $body);
auto me = state->exprs.add<ExprLambda>(state->positions, state->exprs.alloc, CUR_POS, arg, std::move($formal_set), $body);
$$ = me;
SET_DOC_POS(me, @1);
}
@@ -211,7 +211,7 @@ expr_function
| WITH expr ';' expr_function
{ $$ = state->exprs.add<ExprWith>(CUR_POS, $2, $4); }
| LET binds IN_KW expr_function
{ if (!$2->dynamicAttrs->empty())
{ if (!$2->dynamicAttrs.empty())
throw ParseError({
.msg = HintFmt("dynamic attributes not allowed in let"),
.pos = state->positions[CUR_POS]
@@ -251,7 +251,7 @@ expr_op
| expr_op OR expr_op { $$ = state->exprs.add<ExprOpOr>(state->at(@2), $1, $3); }
| expr_op IMPL expr_op { $$ = state->exprs.add<ExprOpImpl>(state->at(@2), $1, $3); }
| expr_op UPDATE expr_op { $$ = state->exprs.add<ExprOpUpdate>(state->at(@2), $1, $3); }
| expr_op '?' attrpath { $$ = state->exprs.add<ExprOpHasAttr>(state->exprs.alloc, $1, $3); }
| expr_op '?' attrpath { $$ = state->exprs.add<ExprOpHasAttr>(state->exprs.alloc, $1, std::move($3)); }
| expr_op '+' expr_op
{ $$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, state->at(@2), false, {{state->at(@1), $1}, {state->at(@3), $3}}); }
| expr_op '-' expr_op { $$ = state->exprs.add<ExprCall>(state->at(@2), state->exprs.add<ExprVar>(state->s.sub), {$1, $3}); }
@@ -272,9 +272,9 @@ expr_app
expr_select
: expr_simple '.' attrpath
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, $3, nullptr); }
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, std::move($3), nullptr); }
| expr_simple '.' attrpath OR_KW expr_select
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, $3, $5); $5->warnIfCursedOr(state->symbols, state->positions); }
{ $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $1, std::move($3), $5); $5->warnIfCursedOr(state->symbols, state->positions); }
| /* Backwards compatibility: because Nixpkgs has a function named or,
allow stuff like map or [...]. This production is problematic (see
https://github.com/NixOS/nix/issues/11118) and will be refactored in the
@@ -297,14 +297,19 @@ expr_simple
}
| INT_LIT { $$ = state->exprs.add<ExprInt>($1); }
| FLOAT_LIT { $$ = state->exprs.add<ExprFloat>($1); }
| '"' string_parts '"' { $$ = $2.toExpr(state->exprs); }
| '"' string_parts '"' {
std::visit(overloaded{
[&](std::string_view str) { $$ = state->exprs.add<ExprString>(state->exprs.alloc, str); },
[&](Expr * expr) { $$ = expr; }},
$2);
}
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = state->stripIndentation(CUR_POS, $2);
$$ = state->stripIndentation(CUR_POS, std::move($2));
}
| path_start PATH_END
| path_start string_parts_interpolated PATH_END {
$2.insert($2.begin(), {state->at(@1), $1});
$$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, false, $2);
$$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, false, std::move($2));
}
| SPATH {
std::string_view path($1.p + 1, $1.l - 2);
@@ -333,13 +338,13 @@ expr_simple
{ $2->pos = CUR_POS; $$ = $2; }
| '{' '}'
{ $$ = state->exprs.add<ExprAttrs>(CUR_POS); }
| '[' list ']' { $$ = state->exprs.add<ExprList>(state->exprs.alloc, $2); }
| '[' list ']' { $$ = state->exprs.add<ExprList>(state->exprs.alloc, std::move($2)); }
;
string_parts
: STR { $$ = {$1}; }
| string_parts_interpolated { $$ = {state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, true, $1)}; }
| { $$ = {std::string_view()}; }
: STR { $$ = $1; }
| string_parts_interpolated { $$ = state->exprs.add<ExprConcatStrings>(state->exprs.alloc, CUR_POS, true, std::move($1)); }
| { $$ = std::string_view(); }
;
string_parts_interpolated
@@ -408,9 +413,9 @@ binds1
| binds[accum] INHERIT attrs ';'
{ $$ = $accum;
for (auto & [i, iPos] : $attrs) {
if ($accum->attrs->find(i.symbol) != $accum->attrs->end())
state->dupAttr(i.symbol, iPos, (*$accum->attrs)[i.symbol].pos);
$accum->attrs->emplace(
if ($accum->attrs.find(i.symbol) != $accum->attrs.end())
state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos);
$accum->attrs.emplace(
i.symbol,
ExprAttrs::AttrDef(state->exprs.add<ExprVar>(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
}
@@ -418,13 +423,13 @@ binds1
| binds[accum] INHERIT '(' expr ')' attrs ';'
{ $$ = $accum;
if (!$accum->inheritFromExprs)
$accum->inheritFromExprs = std::make_unique<std::pmr::vector<Expr *>>();
$accum->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
$accum->inheritFromExprs->push_back($expr);
auto from = state->exprs.add<ExprInheritFrom>(state->at(@expr), $accum->inheritFromExprs->size() - 1);
auto from = new nix::ExprInheritFrom(state->at(@expr), $accum->inheritFromExprs->size() - 1);
for (auto & [i, iPos] : $attrs) {
if ($accum->attrs->find(i.symbol) != $accum->attrs->end())
state->dupAttr(i.symbol, iPos, (*$accum->attrs)[i.symbol].pos);
$accum->attrs->emplace(
if ($accum->attrs.find(i.symbol) != $accum->attrs.end())
state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos);
$accum->attrs.emplace(
i.symbol,
ExprAttrs::AttrDef(
state->exprs.add<ExprSelect>(state->exprs.alloc, iPos, from, i.symbol),
@@ -442,15 +447,15 @@ attrs
: attrs attr { $$ = std::move($1); $$.emplace_back(state->symbols.create($2), state->at(@2)); }
| attrs string_attr
{ $$ = std::move($1);
$2.visit(overloaded{
std::visit(overloaded {
[&](std::string_view str) { $$.emplace_back(state->symbols.create(str), state->at(@2)); },
[&](Expr * expr) {
throw ParseError({
.msg = HintFmt("dynamic attributes not allowed in inherit"),
.pos = state->positions[state->at(@2)]
});
}}
);
throw ParseError({
.msg = HintFmt("dynamic attributes not allowed in inherit"),
.pos = state->positions[state->at(@2)]
});
}
}, $2);
}
| { }
;
@@ -459,17 +464,17 @@ attrpath
: attrpath '.' attr { $$ = std::move($1); $$.emplace_back(state->symbols.create($3)); }
| attrpath '.' string_attr
{ $$ = std::move($1);
$3.visit(overloaded{
std::visit(overloaded {
[&](std::string_view str) { $$.emplace_back(state->symbols.create(str)); },
[&](Expr * expr) { $$.emplace_back(expr); }}
);
[&](Expr * expr) { $$.emplace_back(expr); }
}, std::move($3));
}
| attr { $$.emplace_back(state->symbols.create($1)); }
| string_attr
{ $1.visit(overloaded{
{ std::visit(overloaded {
[&](std::string_view str) { $$.emplace_back(state->symbols.create(str)); },
[&](Expr * expr) { $$.emplace_back(expr); }}
);
[&](Expr * expr) { $$.emplace_back(expr); }
}, std::move($1));
}
;
@@ -480,7 +485,7 @@ attr
string_attr
: '"' string_parts '"' { $$ = std::move($2); }
| DOLLAR_CURLY expr '}' { $$ = {$2}; }
| DOLLAR_CURLY expr '}' { $$ = $2; }
;
list

View File

@@ -5,7 +5,6 @@
#include "nix/expr/eval-settings.hh"
#include "nix/expr/gc-small-vector.hh"
#include "nix/expr/json-to-value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/store/globals.hh"
#include "nix/store/names.hh"
#include "nix/store/path-references.hh"
@@ -40,12 +39,6 @@
namespace nix {
RegisterPrimOp::PrimOps & RegisterPrimOp::primOps()
{
static RegisterPrimOp::PrimOps primOps;
return primOps;
}
/*************************************************************
* Miscellaneous
*************************************************************/
@@ -53,7 +46,7 @@ RegisterPrimOp::PrimOps & RegisterPrimOp::primOps()
static inline Value * mkString(EvalState & state, const std::csub_match & match)
{
Value * v = state.allocValue();
v->mkString({match.first, match.second}, state.mem);
v->mkString({match.first, match.second});
return v;
}
@@ -228,14 +221,13 @@ void derivationToValue(
path2,
{
NixStringContextElem::DrvDeep{.drvPath = storePath},
},
state.mem);
attrs.alloc(state.s.name).mkString(drv.env["name"], state.mem);
});
attrs.alloc(state.s.name).mkString(drv.env["name"]);
auto list = state.buildList(drv.outputs.size());
for (const auto & [i, o] : enumerate(drv.outputs)) {
mkOutputString(state, attrs, storePath, o);
(list[i] = state.allocValue())->mkString(o.first, state.mem);
(list[i] = state.allocValue())->mkString(o.first);
}
attrs.alloc(state.s.outputs).mkList(list);
@@ -495,34 +487,34 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value ** args, Valu
state.forceValue(*args[0], pos);
switch (args[0]->type()) {
case nInt:
v.mkStringNoCopy("int"_sds);
v.mkStringNoCopy("int");
break;
case nBool:
v.mkStringNoCopy("bool"_sds);
v.mkStringNoCopy("bool");
break;
case nString:
v.mkStringNoCopy("string"_sds);
v.mkStringNoCopy("string");
break;
case nPath:
v.mkStringNoCopy("path"_sds);
v.mkStringNoCopy("path");
break;
case nNull:
v.mkStringNoCopy("null"_sds);
v.mkStringNoCopy("null");
break;
case nAttrs:
v.mkStringNoCopy("set"_sds);
v.mkStringNoCopy("set");
break;
case nList:
v.mkStringNoCopy("list"_sds);
v.mkStringNoCopy("list");
break;
case nFunction:
v.mkStringNoCopy("lambda"_sds);
v.mkStringNoCopy("lambda");
break;
case nExternal:
v.mkString(args[0]->external()->typeOf(), state.mem);
v.mkString(args[0]->external()->typeOf());
break;
case nFloat:
v.mkStringNoCopy("float"_sds);
v.mkStringNoCopy("float");
break;
case nThunk:
unreachable();
@@ -1176,7 +1168,7 @@ static void prim_getEnv(EvalState & state, const PosIdx pos, Value ** args, Valu
{
std::string name(
state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getEnv"));
v.mkString(state.settings.restrictEval || state.settings.pureEval ? "" : getEnv(name).value_or(""), state.mem);
v.mkString(state.settings.restrictEval || state.settings.pureEval ? "" : getEnv(name).value_or(""));
}
static RegisterPrimOp primop_getEnv({
@@ -1774,7 +1766,28 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
drv.outputs.insert_or_assign(i, DerivationOutput::Deferred{});
}
drv.fillInOutputPaths(*state.store);
auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true);
switch (hashModulo.kind) {
case DrvHash::Kind::Regular:
for (auto & i : outputs) {
auto h = get(hashModulo.hashes, i);
if (!h)
state.error<AssertionError>("derivation produced no hash for output '%s'", i).atPos(v).debugThrow();
auto outPath = state.store->makeOutputPath(i, *h, drvName);
drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign(
i,
DerivationOutput::InputAddressed{
.path = std::move(outPath),
});
}
break;
;
case DrvHash::Kind::Deferred:
for (auto & i : outputs) {
drv.outputs.insert_or_assign(i, DerivationOutput::Deferred{});
}
}
}
/* Write the resulting term into the Nix store directory. */
@@ -1797,8 +1810,7 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
drvPathS,
{
NixStringContextElem::DrvDeep{.drvPath = drvPath},
},
state.mem);
});
for (auto & i : drv.outputs)
mkOutputString(state, result, drvPath, i);
@@ -1821,10 +1833,8 @@ static RegisterPrimOp primop_derivationStrict(
out. */
static void prim_placeholder(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
v.mkString(
hashPlaceholder(state.forceStringNoCtx(
*args[0], pos, "while evaluating the first argument passed to builtins.placeholder")),
state.mem);
v.mkString(hashPlaceholder(
state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.placeholder")));
}
static RegisterPrimOp primop_placeholder({
@@ -1853,7 +1863,7 @@ static void prim_toPath(EvalState & state, const PosIdx pos, Value ** args, Valu
NixStringContext context;
auto path =
state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.toPath");
v.mkString(path.path.abs(), context, state.mem);
v.mkString(path.path.abs(), context);
}
static RegisterPrimOp primop_toPath({
@@ -1896,7 +1906,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value ** args, V
if (!settings.readOnlyMode)
state.store->ensurePath(path2);
context.insert(NixStringContextElem::Opaque{.path = path2});
v.mkString(path.abs(), context, state.mem);
v.mkString(path.abs(), context);
}
static RegisterPrimOp primop_storePath({
@@ -1978,8 +1988,7 @@ static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value ** args,
v.mkString(
legacyBaseNameOf(*state.coerceToString(
pos, *args[0], context, "while evaluating the first argument passed to builtins.baseNameOf", false, false)),
context,
state.mem);
context);
}
static RegisterPrimOp primop_baseNameOf({
@@ -2008,18 +2017,18 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, Value
state.forceValue(*args[0], pos);
if (args[0]->type() == nPath) {
auto path = args[0]->path();
v.mkPath(path.path.isRoot() ? path : path.parent(), state.mem);
v.mkPath(path.path.isRoot() ? path : path.parent());
} else {
NixStringContext context;
auto path = state.coerceToString(
pos, *args[0], context, "while evaluating the first argument passed to 'builtins.dirOf'", false, false);
auto pos = path->rfind('/');
if (pos == path->npos)
v.mkStringMove("."_sds, context, state.mem);
v.mkStringMove(".", context);
else if (pos == 0)
v.mkStringMove("/"_sds, context, state.mem);
v.mkStringMove("/", context);
else
v.mkString(path->substr(0, pos), context, state.mem);
v.mkString(path->substr(0, pos), context);
}
}
@@ -2061,7 +2070,7 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value ** args, Va
.path = std::move((StorePath &&) p),
});
}
v.mkString(s, context, state.mem);
v.mkString(s, context);
}
static RegisterPrimOp primop_readFile({
@@ -2125,7 +2134,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value ** args, Va
auto path =
state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.findFile");
v.mkPath(state.findFile(lookupPath, path, pos), state.mem);
v.mkPath(state.findFile(lookupPath, path, pos));
}
static RegisterPrimOp primop_findFile(
@@ -2274,7 +2283,7 @@ static void prim_hashFile(EvalState & state, const PosIdx pos, Value ** args, Va
auto path = realisePath(state, pos, *args[1]);
v.mkString(hashString(*ha, path.readFile()).to_string(HashFormat::Base16, false), state.mem);
v.mkString(hashString(*ha, path.readFile()).to_string(HashFormat::Base16, false));
}
static RegisterPrimOp primop_hashFile({
@@ -2300,10 +2309,10 @@ static const Value & fileTypeToString(EvalState & state, SourceAccessor::Type ty
static const Constants stringValues = []() {
Constants res;
res.regular.mkStringNoCopy("regular"_sds);
res.directory.mkStringNoCopy("directory"_sds);
res.symlink.mkStringNoCopy("symlink"_sds);
res.unknown.mkStringNoCopy("unknown"_sds);
res.regular.mkStringNoCopy("regular");
res.directory.mkStringNoCopy("directory");
res.symlink.mkStringNoCopy("symlink");
res.unknown.mkStringNoCopy("unknown");
return res;
}();
@@ -2363,7 +2372,7 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value ** args, Val
// detailed node info quickly in this case we produce a thunk to
// query the file type lazily.
auto epath = state.allocValue();
epath->mkPath(path / name, state.mem);
epath->mkPath(path / name);
if (!readFileType)
readFileType = &state.getBuiltin("readFileType");
attr.mkApp(readFileType, epath);
@@ -2457,7 +2466,7 @@ static void prim_toXML(EvalState & state, const PosIdx pos, Value ** args, Value
std::ostringstream out;
NixStringContext context;
printValueAsXML(state, true, false, *args[0], out, context, pos);
v.mkString(out.view(), context, state.mem);
v.mkString(out.view(), context);
}
static RegisterPrimOp primop_toXML({
@@ -2565,7 +2574,7 @@ static void prim_toJSON(EvalState & state, const PosIdx pos, Value ** args, Valu
std::ostringstream out;
NixStringContext context;
printValueAsJSON(state, true, *args[0], pos, out, context);
v.mkString(out.view(), context, state.mem);
v.mkString(out.view(), context);
}
static RegisterPrimOp primop_toJSON({
@@ -2744,7 +2753,7 @@ bool EvalState::callPathFilter(Value * filterFun, const SourcePath & path, PosId
/* Call the filter function. The first argument is the path, the
second is a string indicating the type of the file. */
Value arg1;
arg1.mkString(path.path.abs(), mem);
arg1.mkString(path.path.abs());
// assert that type is not "unknown"
Value * args[]{&arg1, const_cast<Value *>(&fileTypeToString(*this, st.type))};
@@ -4394,7 +4403,7 @@ static void prim_toString(EvalState & state, const PosIdx pos, Value ** args, Va
NixStringContext context;
auto s = state.coerceToString(
pos, *args[0], context, "while evaluating the first argument passed to builtins.toString", true, false);
v.mkString(*s, context, state.mem);
v.mkString(*s, context);
}
static RegisterPrimOp primop_toString({
@@ -4454,7 +4463,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value ** args, V
if (len == 0) {
state.forceValue(*args[2], pos);
if (args[2]->type() == nString) {
v.mkStringNoCopy(""_sds, args[2]->context());
v.mkStringNoCopy("", args[2]->context());
return;
}
}
@@ -4467,7 +4476,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value ** args, V
auto s = state.coerceToString(
pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
v.mkString(NixUInt(start) >= s->size() ? "" : s->substr(start, _len), context, state.mem);
v.mkString(NixUInt(start) >= s->size() ? "" : s->substr(start, _len), context);
}
static RegisterPrimOp primop_substring({
@@ -4522,7 +4531,7 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value ** args,
auto s =
state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.hashString");
v.mkString(hashString(*ha, s).to_string(HashFormat::Base16, false), state.mem);
v.mkString(hashString(*ha, s).to_string(HashFormat::Base16, false));
}
static RegisterPrimOp primop_hashString({
@@ -4555,7 +4564,7 @@ static void prim_convertHash(EvalState & state, const PosIdx pos, Value ** args,
HashFormat hf = parseHashFormat(
state.forceStringNoCtx(*iteratorToHashFormat->value, pos, "while evaluating the attribute 'toHashFormat'"));
v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI), state.mem);
v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI));
}
static RegisterPrimOp primop_convertHash({
@@ -4865,7 +4874,7 @@ static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value **
"while evaluating one element of the list of strings to concat passed to builtins.concatStringsSep");
}
v.mkString(res, context, state.mem);
v.mkString(res, context);
}
static RegisterPrimOp primop_concatStringsSep({
@@ -4940,7 +4949,7 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value ** ar
}
}
v.mkString(res, context, state.mem);
v.mkString(res, context);
}
static RegisterPrimOp primop_replaceStrings({
@@ -4973,8 +4982,8 @@ static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value ** args
state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.parseDrvName");
DrvName parsed(name);
auto attrs = state.buildBindings(2);
attrs.alloc(state.s.name).mkString(parsed.name, state.mem);
attrs.alloc("version").mkString(parsed.version, state.mem);
attrs.alloc(state.s.name).mkString(parsed.name);
attrs.alloc("version").mkString(parsed.version);
v.mkAttrs(attrs);
}
@@ -5029,7 +5038,7 @@ static void prim_splitVersion(EvalState & state, const PosIdx pos, Value ** args
}
auto list = state.buildList(components.size());
for (const auto & [n, component] : enumerate(components))
(list[n] = state.allocValue())->mkString(std::move(component), state.mem);
(list[n] = state.allocValue())->mkString(std::move(component));
v.mkList(list);
}
@@ -5173,7 +5182,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
});
if (!settings.pureEval)
v.mkString(settings.getCurrentSystem(), mem);
v.mkString(settings.getCurrentSystem());
addConstant(
"__currentSystem",
v,
@@ -5205,7 +5214,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
.impureOnly = true,
});
v.mkString(nixVersion, mem);
v.mkString(nixVersion);
addConstant(
"__nixVersion",
v,
@@ -5230,7 +5239,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
)",
});
v.mkString(store->storeDir, mem);
v.mkString(store->storeDir);
addConstant(
"__storeDir",
v,
@@ -5295,8 +5304,8 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
auto list = buildList(lookupPath.elements.size());
for (const auto & [n, i] : enumerate(lookupPath.elements)) {
auto attrs = buildBindings(2);
attrs.alloc("path").mkString(i.path.s, mem);
attrs.alloc("prefix").mkString(i.prefix.s, mem);
attrs.alloc("path").mkString(i.path.s);
attrs.alloc("prefix").mkString(i.prefix.s);
(list[n] = allocValue())->mkAttrs(attrs);
}
v.mkList(list);

View File

@@ -11,7 +11,7 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos,
NixStringContext context;
auto s = state.coerceToString(
pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
v.mkString(*s, state.mem);
v.mkString(*s);
}
static RegisterPrimOp primop_unsafeDiscardStringContext({
@@ -69,7 +69,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
}
}
v.mkString(*s, context2, state.mem);
v.mkString(*s, context2);
}
static RegisterPrimOp primop_unsafeDiscardOutputDependency(
@@ -137,7 +137,7 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V
context.begin()->raw)}),
};
v.mkString(*s, context2, state.mem);
v.mkString(*s, context2);
}
static RegisterPrimOp primop_addDrvOutputDependencies(
@@ -218,7 +218,7 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args,
if (!info.second.outputs.empty()) {
auto list = state.buildList(info.second.outputs.size());
for (const auto & [i, output] : enumerate(info.second.outputs))
(list[i] = state.allocValue())->mkString(output, state.mem);
(list[i] = state.allocValue())->mkString(output);
infoAttrs.alloc(state.s.outputs).mkList(list);
}
attrs.alloc(state.store->printStorePath(info.first)).mkAttrs(infoAttrs);
@@ -321,7 +321,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value ** arg
}
}
v.mkString(orig, context, state.mem);
v.mkString(orig, context);
}
static RegisterPrimOp primop_appendContext({.name = "__appendContext", .arity = 2, .fun = prim_appendContext});

View File

@@ -81,17 +81,17 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** ar
attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
auto [storePath, input2] = input.fetchToStore(state.fetchSettings, *state.store);
auto [storePath, input2] = input.fetchToStore(state.store);
auto attrs2 = state.buildBindings(8);
state.mkStorePathString(storePath, attrs2.alloc(state.s.outPath));
if (input2.getRef())
attrs2.alloc("branch").mkString(*input2.getRef(), state.mem);
attrs2.alloc("branch").mkString(*input2.getRef());
// Backward compatibility: set 'rev' to
// 0000000000000000000000000000000000000000 for a dirty tree.
auto rev2 = input2.getRev().value_or(Hash(HashAlgorithm::SHA1));
attrs2.alloc("rev").mkString(rev2.gitRev(), state.mem);
attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12), state.mem);
attrs2.alloc("rev").mkString(rev2.gitRev());
attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12));
if (auto revCount = input2.getRevCount())
attrs2.alloc("revCount").mkInt(*revCount);
v.mkAttrs(attrs2);

View File

@@ -35,7 +35,7 @@ void emitTreeAttrs(
// FIXME: support arbitrary input attributes.
if (auto narHash = input.getNarHash())
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true), state.mem);
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
if (input.getType() == "git")
attrs.alloc("submodules").mkBool(fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
@@ -43,13 +43,13 @@ void emitTreeAttrs(
if (!forceDirty) {
if (auto rev = input.getRev()) {
attrs.alloc("rev").mkString(rev->gitRev(), state.mem);
attrs.alloc("shortRev").mkString(rev->gitShortRev(), state.mem);
attrs.alloc("rev").mkString(rev->gitRev());
attrs.alloc("shortRev").mkString(rev->gitShortRev());
} else if (emptyRevFallback) {
// Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
auto emptyHash = Hash(HashAlgorithm::SHA1);
attrs.alloc("rev").mkString(emptyHash.gitRev(), state.mem);
attrs.alloc("shortRev").mkString(emptyHash.gitShortRev(), state.mem);
attrs.alloc("rev").mkString(emptyHash.gitRev());
attrs.alloc("shortRev").mkString(emptyHash.gitShortRev());
}
if (auto revCount = input.getRevCount())
@@ -59,14 +59,13 @@ void emitTreeAttrs(
}
if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) {
attrs.alloc("dirtyRev").mkString(*dirtyRev, state.mem);
attrs.alloc("dirtyShortRev").mkString(*fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev"), state.mem);
attrs.alloc("dirtyRev").mkString(*dirtyRev);
attrs.alloc("dirtyShortRev").mkString(*fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev"));
}
if (auto lastModified = input.getLastModified()) {
attrs.alloc("lastModified").mkInt(*lastModified);
attrs.alloc("lastModifiedDate")
.mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")), state.mem);
attrs.alloc("lastModifiedDate").mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
}
v.mkAttrs(attrs);
@@ -83,7 +82,7 @@ struct FetchTreeParams
static void fetchTree(
EvalState & state, const PosIdx pos, Value ** args, Value & v, const FetchTreeParams & params = FetchTreeParams{})
{
fetchers::Input input{};
fetchers::Input input{state.fetchSettings};
NixStringContext context;
std::optional<std::string> type;
auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree";
@@ -195,9 +194,9 @@ static void fetchTree(
}
if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes))
input = lookupInRegistries(state.fetchSettings, *state.store, input, fetchers::UseRegistries::Limited).first;
input = lookupInRegistries(state.store, input, fetchers::UseRegistries::Limited).first;
if (state.settings.pureEval && !input.isLocked(state.fetchSettings)) {
if (state.settings.pureEval && !input.isLocked()) {
if (input.getNarHash())
warn(
"Input '%s' is unlocked (e.g. lacks a Git revision) but is checked by NAR hash. "
@@ -220,8 +219,7 @@ static void fetchTree(
throw Error("input '%s' is not allowed to use the '__final' attribute", input.to_string());
}
auto cachedInput =
state.inputCache->getAccessor(state.fetchSettings, *state.store, input, fetchers::UseRegistries::No);
auto cachedInput = state.inputCache->getAccessor(state.store, input, fetchers::UseRegistries::No);
auto storePath = state.mountInput(cachedInput.lockedInput, input, cachedInput.accessor);
@@ -236,127 +234,229 @@ static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, V
static RegisterPrimOp primop_fetchTree({
.name = "fetchTree",
.args = {"input"},
.doc = []() -> std::string {
std::string doc = stripIndentation(R"(
Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with:
.doc = R"(
Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with:
- the resulting fixed-output [store path](@docroot@/store/store-path.md)
- the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash
- backend-specific metadata (currently not documented). <!-- TODO: document output attributes -->
- the resulting fixed-output [store path](@docroot@/store/store-path.md)
- the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash
- backend-specific metadata (currently not documented). <!-- TODO: document output attributes -->
*input* must be an attribute set with the following attributes:
*input* must be an attribute set with the following attributes:
- `type` (String, required)
- `type` (String, required)
One of the [supported source types](#source-types).
This determines other required and allowed input attributes.
One of the [supported source types](#source-types).
This determines other required and allowed input attributes.
- `narHash` (String, optional)
- `narHash` (String, optional)
The `narHash` parameter can be used to substitute the source of the tree.
It also allows for verification of tree contents that may not be provided by the underlying transfer mechanism.
If `narHash` is set, the source is first looked up is the Nix store and [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available.
The `narHash` parameter can be used to substitute the source of the tree.
It also allows for verification of tree contents that may not be provided by the underlying transfer mechanism.
If `narHash` is set, the source is first looked up is the Nix store and [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available.
A subset of the output attributes of `fetchTree` can be re-used for subsequent calls to `fetchTree` to produce the same result again.
That is, `fetchTree` is idempotent.
A subset of the output attributes of `fetchTree` can be re-used for subsequent calls to `fetchTree` to produce the same result again.
That is, `fetchTree` is idempotent.
Downloads are cached in `$XDG_CACHE_HOME/nix`.
The remote source is fetched from the network if both are true:
- A NAR hash is supplied and the corresponding store path is not [valid](@docroot@/glossary.md#gloss-validity), that is, not available in the store
Downloads are cached in `$XDG_CACHE_HOME/nix`.
The remote source is fetched from the network if both are true:
- A NAR hash is supplied and the corresponding store path is not [valid](@docroot@/glossary.md#gloss-validity), that is, not available in the store
> **Note**
> **Note**
>
> [Substituters](@docroot@/command-ref/conf-file.md#conf-substituters) are not used in fetching.
- There is no cache entry or the cache entry is older than [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl)
## Source types
The following source types and associated input attributes are supported.
<!-- TODO: It would be soooo much more predictable to work with (and
document) if `fetchTree` was a curried call with the first parameter for
`type` or an attribute like `builtins.fetchTree.git`! -->
- `"file"`
Place a plain file into the Nix store.
This is similar to [`builtins.fetchurl`](@docroot@/language/builtins.md#builtins-fetchurl)
- `url` (String, required)
Supported protocols:
- `https`
> **Example**
>
> [Substituters](@docroot@/command-ref/conf-file.md#conf-substituters) are not used in fetching.
> ```nix
> fetchTree {
> type = "file";
> url = "https://example.com/index.html";
> }
> ```
- There is no cache entry or the cache entry is older than [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl)
- `http`
## Source types
Insecure HTTP transfer for legacy sources.
The following source types and associated input attributes are supported.
> **Warning**
>
> HTTP performs no encryption or authentication.
> Use a `narHash` known in advance to ensure the output has expected contents.
<!-- TODO: It would be soooo much more predictable to work with (and
document) if `fetchTree` was a curried call with the first parameter for
`type` or an attribute like `builtins.fetchTree.git`! -->
)");
- `file`
auto indentString = [](std::string const & str, std::string const & indent) {
std::string result;
std::istringstream stream(str);
std::string line;
bool first = true;
while (std::getline(stream, line)) {
if (!first)
result += "\n";
result += indent + line;
first = false;
}
return result;
};
A file on the local file system.
for (const auto & [schemeName, scheme] : fetchers::getAllInputSchemes()) {
doc += "\n- `" + quoteString(schemeName, '"') + "`\n\n";
doc += indentString(scheme->schemeDescription(), " ");
if (!doc.empty() && doc.back() != '\n')
doc += "\n";
> **Example**
>
> ```nix
> fetchTree {
> type = "file";
> url = "file:///home/eelco/nix/README.md";
> }
> ```
for (const auto & [attrName, attribute] : scheme->allowedAttrs()) {
doc += "\n - `" + attrName + "` (" + attribute.type + ", "
+ (attribute.required ? "required" : "optional") + ")\n\n";
doc += indentString(stripIndentation(attribute.doc), " ");
if (!doc.empty() && doc.back() != '\n')
doc += "\n";
}
}
- `"tarball"`
doc += "\n" + stripIndentation(R"(
The following input types are still subject to change:
Download a tar archive and extract it into the Nix store.
This has the same underlying implementation as [`builtins.fetchTarball`](@docroot@/language/builtins.md#builtins-fetchTarball)
- `"path"`
- `"github"`
- `"gitlab"`
- `"sourcehut"`
- `"mercurial"`
- `url` (String, required)
*input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references).
The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled.
> **Example**
>
> ```nix
> fetchTree {
> type = "tarball";
> url = "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-23.11";
> }
> ```
- `"git"`
Fetch a Git tree and copy it to the Nix store.
This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit).
- `url` (String, required)
The URL formats supported are the same as for Git itself.
> **Example**
>
> Fetch a GitHub repository using the attribute set representation:
>
> ```nix
> builtins.fetchTree {
> type = "github";
> owner = "NixOS";
> repo = "nixpkgs";
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
> }
> ```
>
> This evaluates to the following attribute set:
>
> ```nix
> {
> lastModified = 1686503798;
> lastModifiedDate = "20230611171638";
> narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc=";
> outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source";
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
> shortRev = "ae2e6b3";
> fetchTree {
> type = "git";
> url = "git@github.com:NixOS/nixpkgs.git";
> }
> ```
> **Example**
> **Note**
>
> Fetch the same GitHub repository using the URL-like syntax:
>
> ```nix
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
> ```
)");
> If the URL points to a local directory, and no `ref` or `rev` is given, Nix only considers files added to the Git index, as listed by `git ls-files` but use the *current file contents* of the Git working directory.
return doc;
}(),
- `ref` (String, optional)
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
Default: `"HEAD"`
- `rev` (String, optional)
A Git revision; a commit hash.
Default: the tip of `ref`
- `shallow` (Bool, optional)
Make a shallow clone when fetching the Git tree.
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
Default: `true`
- `submodules` (Bool, optional)
Also fetch submodules if available.
Default: `false`
- `lfs` (Bool, optional)
Fetch any [Git LFS](https://git-lfs.com/) files.
Default: `false`
- `allRefs` (Bool, optional)
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
Whether to fetch all references (eg. branches and tags) of the repository.
With this argument being true, it's possible to load a `rev` from *any* `ref`.
(Without setting this option, only `rev`s from the specified `ref` are supported).
Default: `false`
- `lastModified` (Integer, optional)
Unix timestamp of the fetched commit.
If set, pass through the value to the output attribute set.
Otherwise, generated from the fetched Git tree.
- `revCount` (Integer, optional)
Number of revisions in the history of the Git repository before the fetched commit.
If set, pass through the value to the output attribute set.
Otherwise, generated from the fetched Git tree.
The following input types are still subject to change:
- `"path"`
- `"github"`
- `"gitlab"`
- `"sourcehut"`
- `"mercurial"`
*input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references).
The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled.
> **Example**
>
> Fetch a GitHub repository using the attribute set representation:
>
> ```nix
> builtins.fetchTree {
> type = "github";
> owner = "NixOS";
> repo = "nixpkgs";
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
> }
> ```
>
> This evaluates to the following attribute set:
>
> ```nix
> {
> lastModified = 1686503798;
> lastModifiedDate = "20230611171638";
> narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc=";
> outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source";
> rev = "ae2e6b3958682513d28f7d633734571fb18285dd";
> shortRev = "ae2e6b3";
> }
> ```
> **Example**
>
> Fetch the same GitHub repository using the URL-like syntax:
>
> ```nix
> builtins.fetchTree "github:NixOS/nixpkgs/ae2e6b3958682513d28f7d633734571fb18285dd"
> ```
)",
.fun = prim_fetchTree,
.experimentalFeature = Xp::FetchTree,
});
@@ -481,10 +581,10 @@ static void fetch(
auto storePath = unpack ? fetchToStore(
state.fetchSettings,
*state.store,
fetchers::downloadTarball(*state.store, state.fetchSettings, *url),
fetchers::downloadTarball(state.store, state.fetchSettings, *url),
FetchMode::Copy,
name)
: fetchers::downloadFile(*state.store, state.fetchSettings, *url, name).storePath;
: fetchers::downloadFile(state.store, state.fetchSettings, *url, name).storePath;
if (expectedHash) {
auto hash = unpack ? state.store->queryPathInfo(storePath)->narHash

View File

@@ -1,6 +1,5 @@
#include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/expr/static-string-data.hh"
#include "expr-config-private.hh"
@@ -126,7 +125,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
case toml::value_t::string: {
auto s = toml::get<std::string_view>(t);
forceNoNullByte(s);
v.mkString(s, state.mem);
v.mkString(s);
} break;
case toml::value_t::local_datetime:
case toml::value_t::offset_datetime:
@@ -137,12 +136,12 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
normalizeDatetimeFormat(t);
#endif
auto attrs = state.buildBindings(2);
attrs.alloc("_type").mkStringNoCopy("timestamp"_sds);
attrs.alloc("_type").mkStringNoCopy("timestamp");
std::ostringstream s;
s << t;
auto str = s.view();
forceNoNullByte(str);
attrs.alloc("value").mkString(str, state.mem);
attrs.alloc("value").mkString(str);
v.mkAttrs(attrs);
} else {
throw std::runtime_error("Dates and times are not supported");

View File

@@ -16,8 +16,6 @@ json printValueAsJSON(
{
checkInterrupt();
auto _level = state.addCallDepth(pos);
if (strict)
state.forceValue(v, pos);

View File

@@ -1,19 +1,19 @@
#include "nix/fetchers/git-utils.hh"
#include "nix/util/file-system.hh"
#include <gmock/gmock.h>
#include <git2/global.h>
#include <git2/repository.h>
#include <git2/signature.h>
#include <git2/types.h>
#include <git2/object.h>
#include <git2/tag.h>
#include <git2-experimental/global.h>
#include <git2-experimental/repository.h>
#include <git2-experimental/signature.h>
#include <git2-experimental/types.h>
#include <git2-experimental/object.h>
#include <git2-experimental/tag.h>
#include <gtest/gtest.h>
#include "nix/util/fs-sink.hh"
#include "nix/util/serialise.hh"
#include "nix/fetchers/git-lfs-fetch.hh"
#include <git2/blob.h>
#include <git2/tree.h>
#include <git2-experimental/blob.h>
#include <git2-experimental/tree.h>
namespace nix {
@@ -92,7 +92,7 @@ TEST_F(GitUtilsTest, sink_basic)
// sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello"));
auto result = repo->dereferenceSingletonDirectory(sink->flush());
auto accessor = repo->getAccessor(result, {}, getRepoName());
auto accessor = repo->getAccessor(result, false, getRepoName());
auto entries = accessor->readDirectory(CanonPath::root);
ASSERT_EQ(entries.size(), 5u);
ASSERT_EQ(accessor->readFile(CanonPath("hello")), "hello world");

View File

@@ -5,7 +5,7 @@
#include "nix/fetchers/fetchers.hh"
#include "nix/fetchers/git-utils.hh"
#include <git2.h>
#include <git2-experimental.h>
#include <gtest/gtest.h>
#include <filesystem>
@@ -196,7 +196,7 @@ TEST_F(GitTest, submodulePeriodSupport)
{"ref", "main"},
});
auto [accessor, i] = input.getAccessor(settings, *store);
auto [accessor, i] = input.getAccessor(store);
ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n");
}

View File

@@ -1,61 +0,0 @@
#include "nix/fetchers/fetch-settings.hh"
#include "nix/fetchers/attrs.hh"
#include "nix/fetchers/fetchers.hh"
#include <gtest/gtest.h>
#include <string>
namespace nix {
using fetchers::Attr;
struct InputFromAttrsTestCase
{
fetchers::Attrs attrs;
std::string expectedUrl;
std::string description;
fetchers::Attrs expectedAttrs = attrs;
};
class InputFromAttrsTest : public ::testing::WithParamInterface<InputFromAttrsTestCase>, public ::testing::Test
{};
TEST_P(InputFromAttrsTest, attrsAreCorrectAndRoundTrips)
{
fetchers::Settings fetchSettings;
const auto & testCase = GetParam();
auto input = fetchers::Input::fromAttrs(fetchSettings, fetchers::Attrs(testCase.attrs));
EXPECT_EQ(input.toAttrs(), testCase.expectedAttrs);
EXPECT_EQ(input.toURLString(), testCase.expectedUrl);
auto input2 = fetchers::Input::fromAttrs(fetchSettings, input.toAttrs());
EXPECT_EQ(input, input2);
EXPECT_EQ(input.toAttrs(), input2.toAttrs());
}
INSTANTIATE_TEST_SUITE_P(
InputFromAttrs,
InputFromAttrsTest,
::testing::Values(
// Test for issue #14429.
InputFromAttrsTestCase{
.attrs =
{
{"url", Attr("git+ssh://git@github.com/NixOS/nixpkgs")},
{"type", Attr("git")},
},
.expectedUrl = "git+ssh://git@github.com/NixOS/nixpkgs",
.description = "strips_git_plus_prefix",
.expectedAttrs =
{
{"url", Attr("ssh://git@github.com/NixOS/nixpkgs")},
{"type", Attr("git")},
},
}),
[](const ::testing::TestParamInfo<InputFromAttrsTestCase> & info) { return info.param.description; });
} // namespace nix

View File

@@ -33,7 +33,7 @@ deps_private += rapidcheck
gtest = dependency('gtest', main : true)
deps_private += gtest
libgit2 = dependency('libgit2')
libgit2 = dependency('libgit2-experimental')
deps_private += libgit2
subdir('nix-meson-build-support/common')
@@ -42,7 +42,6 @@ sources = files(
'access-tokens.cc',
'git-utils.cc',
'git.cc',
'input.cc',
'nix_api_fetchers.cc',
'public-key.cc',
)

View File

@@ -1,5 +1,6 @@
#include "nix/fetchers/attrs.hh"
#include "nix/fetchers/fetchers.hh"
#include "nix/fetchers/git-utils.hh"
#include <nlohmann/json.hpp>
@@ -111,7 +112,7 @@ StringMap attrsToQuery(const Attrs & attrs)
Hash getRevAttr(const Attrs & attrs, const std::string & name)
{
return Hash::parseAny(getStrAttr(attrs, name), HashAlgorithm::SHA1);
return parseGitHash(getStrAttr(attrs, name));
}
} // namespace nix::fetchers

View File

@@ -6,7 +6,6 @@
#include "nix/fetchers/fetch-settings.hh"
#include "nix/fetchers/fetch-to-store.hh"
#include "nix/util/url.hh"
#include "nix/util/archive.hh"
#include <nlohmann/json.hpp>
@@ -27,9 +26,18 @@ void registerInputScheme(std::shared_ptr<InputScheme> && inputScheme)
throw Error("Input scheme with name %s already registered", schemeName);
}
const InputSchemeMap & getAllInputSchemes()
nlohmann::json dumpRegisterInputSchemeInfo()
{
return inputSchemes();
using nlohmann::json;
auto res = json::object();
for (auto & [name, scheme] : inputSchemes()) {
auto & r = res[name] = json::object();
r["allowedAttrs"] = scheme->allowedAttrs();
}
return res;
}
Input Input::fromURL(const Settings & settings, const std::string & url, bool requireTree)
@@ -81,7 +89,7 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
// but not all of them. Doing this is to support those other
// operations which are supposed to be robust on
// unknown/uninterpretable inputs.
Input input;
Input input{settings};
input.attrs = attrs;
fixupInput(input);
return input;
@@ -111,7 +119,7 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
return std::move(*res);
}
std::optional<std::string> Input::getFingerprint(Store & store) const
std::optional<std::string> Input::getFingerprint(ref<Store> store) const
{
if (!scheme)
return std::nullopt;
@@ -151,9 +159,9 @@ bool Input::isDirect() const
return !scheme || scheme->isDirect(*this);
}
bool Input::isLocked(const Settings & settings) const
bool Input::isLocked() const
{
return scheme && scheme->isLocked(settings, *this);
return scheme && scheme->isLocked(*this);
}
bool Input::isFinal() const
@@ -190,19 +198,19 @@ bool Input::contains(const Input & other) const
}
// FIXME: remove
std::pair<StorePath, Input> Input::fetchToStore(const Settings & settings, Store & store) const
std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
{
if (!scheme)
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
try {
auto [accessor, result] = getAccessorUnchecked(settings, store);
auto [accessor, result] = getAccessorUnchecked(store);
auto storePath =
nix::fetchToStore(settings, store, SourcePath(accessor), FetchMode::Copy, result.getName());
nix::fetchToStore(*settings, *store, SourcePath(accessor), FetchMode::Copy, result.getName());
auto narHash = store.queryPathInfo(storePath)->narHash;
auto narHash = store->queryPathInfo(storePath)->narHash;
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
@@ -289,10 +297,10 @@ void Input::checkLocks(Input specified, Input & result)
}
}
std::pair<ref<SourceAccessor>, Input> Input::getAccessor(const Settings & settings, Store & store) const
std::pair<ref<SourceAccessor>, Input> Input::getAccessor(ref<Store> store) const
{
try {
auto [accessor, result] = getAccessorUnchecked(settings, store);
auto [accessor, result] = getAccessorUnchecked(store);
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
@@ -305,7 +313,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessor(const Settings & settin
}
}
std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings & settings, Store & store) const
std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> store) const
{
// FIXME: cache the accessor
@@ -325,13 +333,13 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings
*/
if (isFinal() && getNarHash()) {
try {
auto storePath = computeStorePath(store);
auto storePath = computeStorePath(*store);
store.ensurePath(storePath);
store->ensurePath(storePath);
debug("using substituted/cached input '%s' in '%s'", to_string(), store.printStorePath(storePath));
debug("using substituted/cached input '%s' in '%s'", to_string(), store->printStorePath(storePath));
auto accessor = store.requireStoreObjectAccessor(storePath);
auto accessor = store->requireStoreObjectAccessor(storePath);
accessor->fingerprint = getFingerprint(store);
@@ -341,7 +349,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings
if (accessor->fingerprint) {
ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive;
auto cacheKey = makeFetchToStoreCacheKey(getName(), *accessor->fingerprint, method, "/");
settings.getCache()->upsert(cacheKey, store, {}, storePath);
settings->getCache()->upsert(cacheKey, *store, {}, storePath);
}
accessor->setPathDisplay("«" + to_string() + "»");
@@ -352,7 +360,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(const Settings
}
}
auto [accessor, result] = scheme->getAccessor(settings, store, *this);
auto [accessor, result] = scheme->getAccessor(store, *this);
if (!accessor->fingerprint)
accessor->fingerprint = result.getFingerprint(store);
@@ -369,10 +377,10 @@ Input Input::applyOverrides(std::optional<std::string> ref, std::optional<Hash>
return scheme->applyOverrides(*this, ref, rev);
}
void Input::clone(const Settings & settings, Store & store, const std::filesystem::path & destDir) const
void Input::clone(const Path & destDir) const
{
assert(scheme);
scheme->clone(settings, store, *this, destDir);
scheme->clone(*this, destDir);
}
std::optional<std::filesystem::path> Input::getSourcePath() const
@@ -439,6 +447,9 @@ std::optional<Hash> Input::getRev() const
} catch (BadHash & e) {
// Default to sha1 for backwards compatibility with existing
// usages (e.g. `builtins.fetchTree` calls or flake inputs).
//
// Note that means that for SHA-256 git repos, prefixing
// must be used.
hash = Hash::parseAny(*s, HashAlgorithm::SHA1);
}
}
@@ -485,19 +496,9 @@ void InputScheme::putFile(
throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path);
}
void InputScheme::clone(
const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir) const
void InputScheme::clone(const Input & input, const Path & destDir) const
{
if (std::filesystem::exists(destDir))
throw Error("cannot clone into existing path %s", destDir);
auto [accessor, input2] = getAccessor(settings, store, input);
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s' to %s...", input2.to_string(), destDir));
RestoreSink sink(/*startFsync=*/false);
sink.dstPath = destDir;
copyRecursive(*accessor, CanonPath::root, sink, CanonPath::root);
throw Error("do not know how to clone input '%s'", input.to_string());
}
std::optional<ExperimentalFeature> InputScheme::experimentalFeature() const

View File

@@ -7,10 +7,10 @@
#include "nix/util/hash.hh"
#include "nix/store/ssh.hh"
#include <git2/attr.h>
#include <git2/config.h>
#include <git2/errors.h>
#include <git2/remote.h>
#include <git2-experimental/attr.h>
#include <git2-experimental/config.h>
#include <git2-experimental/errors.h>
#include <git2-experimental/remote.h>
#include <nlohmann/json.hpp>
@@ -209,7 +209,7 @@ std::vector<nlohmann::json> Fetch::fetchUrls(const std::vector<Pointer> & pointe
auto url = api.endpoint + "/objects/batch";
const auto & authHeader = api.authHeader;
FileTransferRequest request(parseURL(url));
request.method = HttpMethod::Post;
request.method = HttpMethod::POST;
Headers headers;
if (authHeader.has_value())
headers.push_back({"Authorization", *authHeader});

View File

@@ -13,27 +13,27 @@
#include "nix/util/thread-pool.hh"
#include "nix/util/pool.hh"
#include <git2/attr.h>
#include <git2/blob.h>
#include <git2/branch.h>
#include <git2/commit.h>
#include <git2/config.h>
#include <git2/describe.h>
#include <git2/errors.h>
#include <git2/global.h>
#include <git2/indexer.h>
#include <git2/object.h>
#include <git2/odb.h>
#include <git2/refs.h>
#include <git2/remote.h>
#include <git2/repository.h>
#include <git2/revparse.h>
#include <git2/status.h>
#include <git2/submodule.h>
#include <git2/sys/odb_backend.h>
#include <git2/sys/mempack.h>
#include <git2/tag.h>
#include <git2/tree.h>
#include <git2-experimental/attr.h>
#include <git2-experimental/blob.h>
#include <git2-experimental/branch.h>
#include <git2-experimental/commit.h>
#include <git2-experimental/config.h>
#include <git2-experimental/describe.h>
#include <git2-experimental/errors.h>
#include <git2-experimental/global.h>
#include <git2-experimental/indexer.h>
#include <git2-experimental/object.h>
#include <git2-experimental/odb.h>
#include <git2-experimental/refs.h>
#include <git2-experimental/remote.h>
#include <git2-experimental/repository.h>
#include <git2-experimental/revparse.h>
#include <git2-experimental/status.h>
#include <git2-experimental/submodule.h>
#include <git2-experimental/sys/odb_backend.h>
#include <git2-experimental/sys/mempack.h>
#include <git2-experimental/tag.h>
#include <git2-experimental/tree.h>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
@@ -91,10 +91,21 @@ typedef std::unique_ptr<git_indexer, Deleter<git_indexer_free>> Indexer;
Hash toHash(const git_oid & oid)
{
#ifdef GIT_EXPERIMENTAL_SHA256
assert(oid.type == GIT_OID_SHA1);
#endif
Hash hash(HashAlgorithm::SHA1);
HashAlgorithm algo;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (oid.type) {
case GIT_OID_SHA1:
algo = HashAlgorithm::SHA1;
break;
case GIT_OID_SHA256:
algo = HashAlgorithm::SHA256;
break;
default:
unreachable();
}
#pragma GCC diagnostic pop
Hash hash(algo);
memcpy(hash.hash, oid.id, hash.hashSize);
return hash;
}
@@ -111,7 +122,21 @@ static void initLibGit2()
git_oid hashToOID(const Hash & hash)
{
git_oid oid;
if (git_oid_fromstr(&oid, hash.gitRev().c_str()))
git_oid_t t;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (hash.algo) {
case HashAlgorithm::SHA1:
t = GIT_OID_SHA1;
break;
case HashAlgorithm::SHA256:
t = GIT_OID_SHA256;
break;
default:
throw Error("unsupported hash algorithm for Git: %s", printHashAlgo(hash.algo));
}
#pragma GCC diagnostic pop
if (git_oid_fromstr(&oid, hash.gitRev().c_str(), t))
throw Error("cannot convert '%s' to a Git OID", hash.gitRev());
return oid;
}
@@ -304,7 +329,8 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
// (synchronously on the git_packbuilder_write_buf thread)
Indexer indexer;
git_indexer_progress stats;
if (git_indexer_new(Setter(indexer), pack_dir_path.c_str(), 0, nullptr, nullptr))
git_indexer_options indexer_opts = GIT_INDEXER_OPTIONS_INIT;
if (git_indexer_new(Setter(indexer), pack_dir_path.c_str(), &indexer_opts))
throw Error("creating git packfile indexer: %s", git_error_last()->message);
// TODO: provide index callback for checkInterrupt() termination
@@ -541,15 +567,14 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
}
/**
* A 'GitSourceAccessor' with no regard for export-ignore.
* A 'GitSourceAccessor' with no regard for export-ignore or any other transformations.
*/
ref<GitSourceAccessor> getRawAccessor(const Hash & rev, const GitAccessorOptions & options);
ref<GitSourceAccessor> getRawAccessor(const Hash & rev, bool smudgeLfs = false);
ref<SourceAccessor>
getAccessor(const Hash & rev, const GitAccessorOptions & options, std::string displayPrefix) override;
getAccessor(const Hash & rev, bool exportIgnore, std::string displayPrefix, bool smudgeLfs = false) override;
ref<SourceAccessor>
getAccessor(const WorkdirInfo & wd, const GitAccessorOptions & options, MakeNotAllowedError e) override;
ref<SourceAccessor> getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError e) override;
ref<GitFileSystemObjectSink> getFileSystemObjectSink() override;
@@ -669,7 +694,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
Hash treeHashToNarHash(const fetchers::Settings & settings, const Hash & treeHash) override
{
auto accessor = getAccessor(treeHash, {}, "");
auto accessor = getAccessor(treeHash, false, "");
fetchers::Cache::Key cacheKey{"treeHashToNarHash", {{"treeHash", treeHash.gitRev()}}};
@@ -717,17 +742,15 @@ struct GitSourceAccessor : SourceAccessor
ref<GitRepoImpl> repo;
Object root;
std::optional<lfs::Fetch> lfsFetch = std::nullopt;
GitAccessorOptions options;
};
Sync<State> state_;
GitSourceAccessor(ref<GitRepoImpl> repo_, const Hash & rev, const GitAccessorOptions & options)
GitSourceAccessor(ref<GitRepoImpl> repo_, const Hash & rev, bool smudgeLfs)
: state_{State{
.repo = repo_,
.root = peelToTreeOrBlob(lookupObject(*repo_, hashToOID(rev)).get()),
.lfsFetch = options.smudgeLfs ? std::make_optional(lfs::Fetch(*repo_, hashToOID(rev))) : std::nullopt,
.options = options,
.lfsFetch = smudgeLfs ? std::make_optional(lfs::Fetch(*repo_, hashToOID(rev))) : std::nullopt,
}}
{
}
@@ -1257,26 +1280,26 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
}
};
ref<GitSourceAccessor> GitRepoImpl::getRawAccessor(const Hash & rev, const GitAccessorOptions & options)
ref<GitSourceAccessor> GitRepoImpl::getRawAccessor(const Hash & rev, bool smudgeLfs)
{
auto self = ref<GitRepoImpl>(shared_from_this());
return make_ref<GitSourceAccessor>(self, rev, options);
return make_ref<GitSourceAccessor>(self, rev, smudgeLfs);
}
ref<SourceAccessor>
GitRepoImpl::getAccessor(const Hash & rev, const GitAccessorOptions & options, std::string displayPrefix)
GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore, std::string displayPrefix, bool smudgeLfs)
{
auto self = ref<GitRepoImpl>(shared_from_this());
ref<GitSourceAccessor> rawGitAccessor = getRawAccessor(rev, options);
ref<GitSourceAccessor> rawGitAccessor = getRawAccessor(rev, smudgeLfs);
rawGitAccessor->setPathDisplay(std::move(displayPrefix));
if (options.exportIgnore)
if (exportIgnore)
return make_ref<GitExportIgnoreSourceAccessor>(self, rawGitAccessor, rev);
else
return rawGitAccessor;
}
ref<SourceAccessor> GitRepoImpl::getAccessor(
const WorkdirInfo & wd, const GitAccessorOptions & options, MakeNotAllowedError makeNotAllowedError)
ref<SourceAccessor>
GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError)
{
auto self = ref<GitRepoImpl>(shared_from_this());
ref<SourceAccessor> fileAccessor = AllowListSourceAccessor::create(
@@ -1286,9 +1309,10 @@ ref<SourceAccessor> GitRepoImpl::getAccessor(
boost::unordered_flat_set<CanonPath>{CanonPath::root},
std::move(makeNotAllowedError))
.cast<SourceAccessor>();
if (options.exportIgnore)
fileAccessor = make_ref<GitExportIgnoreSourceAccessor>(self, fileAccessor, std::nullopt);
return fileAccessor;
if (exportIgnore)
return make_ref<GitExportIgnoreSourceAccessor>(self, fileAccessor, std::nullopt);
else
return fileAccessor;
}
ref<GitFileSystemObjectSink> GitRepoImpl::getFileSystemObjectSink()
@@ -1301,7 +1325,7 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
/* Read the .gitmodules files from this revision. */
CanonPath modulesFile(".gitmodules");
auto accessor = getAccessor(rev, {.exportIgnore = exportIgnore}, "");
auto accessor = getAccessor(rev, exportIgnore, "");
if (!accessor->pathExists(modulesFile))
return {};
@@ -1318,7 +1342,7 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
std::vector<std::tuple<Submodule, Hash>> result;
auto rawAccessor = getRawAccessor(rev, {});
auto rawAccessor = getRawAccessor(rev);
for (auto & submodule : parseSubmodules(pathTemp)) {
/* Filter out .gitmodules entries that don't exist or are not
@@ -1330,16 +1354,13 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
return result;
}
namespace fetchers {
ref<GitRepo> Settings::getTarballCache() const
ref<GitRepo> getTarballCache()
{
static auto repoDir = std::filesystem::path(getCacheDir()) / "tarball-cache";
return GitRepo::openRepo(repoDir, true, true);
}
} // namespace fetchers
GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path & path)
{
static Sync<std::map<std::filesystem::path, WorkdirInfo>> _cache;
@@ -1383,4 +1404,21 @@ bool isLegalRefName(const std::string & refName)
return false;
}
Hash parseGitHash(std::string_view hashStr)
{
HashAlgorithm algo;
switch (hashStr.size()) {
case 40:
algo = HashAlgorithm::SHA1;
break;
case 64:
algo = HashAlgorithm::SHA256;
break;
default:
throw Error(
"invalid git hash '%s': expected 40 (SHA1) or 64 (SHA256) hex characters, got %d", hashStr, hashStr.size());
}
return Hash::parseNonSRIUnprefixed(hashStr, algo);
}
} // namespace nix

View File

@@ -168,6 +168,8 @@ struct GitInputScheme : InputScheme
return {};
auto url2(url);
if (hasPrefix(url2.scheme, "git+"))
url2.scheme = std::string(url2.scheme, 4);
url2.query.clear();
Attrs attrs;
@@ -194,183 +196,28 @@ struct GitInputScheme : InputScheme
return "git";
}
std::string schemeDescription() const override
StringSet allowedAttrs() const override
{
return stripIndentation(R"(
Fetch a Git tree and copy it to the Nix store.
This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit).
)");
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = {
{
"url",
{
.type = "String",
.required = true,
.doc = R"(
The URL formats supported are the same as for Git itself.
> **Example**
>
> ```nix
> fetchTree {
> type = "git";
> url = "git@github.com:NixOS/nixpkgs.git";
> }
> ```
> **Note**
>
> If the URL points to a local directory, and no `ref` or `rev` is given, Nix only considers files added to the Git index, as listed by `git ls-files` but uses the *current file contents* of the Git working directory.
)",
},
},
{
"ref",
{
.type = "String",
.required = false,
.doc = R"(
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
Default: `"HEAD"`
)",
},
},
{
"rev",
{
.type = "String",
.required = false,
.doc = R"(
A Git revision; a commit hash.
Default: the tip of `ref`
)",
},
},
{
"shallow",
{
.type = "Bool",
.required = false,
.doc = R"(
Make a shallow clone when fetching the Git tree.
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
Default: `true`
)",
},
},
{
"submodules",
{
.type = "Bool",
.required = false,
.doc = R"(
Also fetch submodules if available.
Default: `false`
)",
},
},
{
"lfs",
{
.type = "Bool",
.required = false,
.doc = R"(
Fetch any [Git LFS](https://git-lfs.com/) files.
Default: `false`
)",
},
},
{
"exportIgnore",
{},
},
{
"lastModified",
{
.type = "Integer",
.required = false,
.doc = R"(
Unix timestamp of the fetched commit.
If set, pass through the value to the output attribute set.
Otherwise, generated from the fetched Git tree.
)",
},
},
{
"revCount",
{
.type = "Integer",
.required = false,
.doc = R"(
Number of revisions in the history of the Git repository before the fetched commit.
If set, pass through the value to the output attribute set.
Otherwise, generated from the fetched Git tree.
)",
},
},
{
"narHash",
{},
},
{
"allRefs",
{
.type = "Bool",
.required = false,
.doc = R"(
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
Whether to fetch all references (eg. branches and tags) of the repository.
With this argument being true, it's possible to load a `rev` from *any* `ref`.
(Without setting this option, only `rev`s from the specified `ref` are supported).
Default: `false`
)",
},
},
{
"name",
{},
},
{
"dirtyRev",
{},
},
{
"dirtyShortRev",
{},
},
{
"verifyCommit",
{},
},
{
"keytype",
{},
},
{
"publicKey",
{},
},
{
"publicKeys",
{},
},
return {
"url",
"ref",
"rev",
"shallow",
"submodules",
"lfs",
"exportIgnore",
"lastModified",
"revCount",
"narHash",
"allRefs",
"name",
"dirtyRev",
"dirtyShortRev",
"verifyCommit",
"keytype",
"publicKey",
"publicKeys",
};
return attrs;
}
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -384,7 +231,7 @@ struct GitInputScheme : InputScheme
if (auto ref = maybeGetStrAttr(attrs, "ref"); ref && !isLegalRefName(*ref))
throw BadURL("invalid Git branch/tag name '%s'", *ref);
Input input{};
Input input{settings};
input.attrs = attrs;
input.attrs["url"] = fixGitURL(getStrAttr(attrs, "url")).to_string();
getShallowAttr(input);
@@ -433,8 +280,7 @@ struct GitInputScheme : InputScheme
return res;
}
void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
const override
void clone(const Input & input, const Path & destDir) const override
{
auto repoInfo = getRepoInfo(input);
@@ -779,7 +625,7 @@ struct GitInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessorFromCommit(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const
getAccessorFromCommit(ref<Store> store, RepoInfo & repoInfo, Input && input) const
{
assert(!repoInfo.workdirInfo.isDirty);
@@ -889,10 +735,10 @@ struct GitInputScheme : InputScheme
auto rev = *input.getRev();
input.attrs.insert_or_assign("lastModified", getLastModified(settings, repoInfo, repoDir, rev));
input.attrs.insert_or_assign("lastModified", getLastModified(*input.settings, repoInfo, repoDir, rev));
if (!getShallowAttr(input))
input.attrs.insert_or_assign("revCount", getRevCount(settings, repoInfo, repoDir, rev));
input.attrs.insert_or_assign("revCount", getRevCount(*input.settings, repoInfo, repoDir, rev));
printTalkative("using revision %s of repo '%s'", rev.gitRev(), repoInfo.locationToArg());
@@ -900,8 +746,7 @@ struct GitInputScheme : InputScheme
bool exportIgnore = getExportIgnoreAttr(input);
bool smudgeLfs = getLfsAttr(input);
auto accessor = repo->getAccessor(
rev, {.exportIgnore = exportIgnore, .smudgeLfs = smudgeLfs}, "«" + input.to_string() + "»");
auto accessor = repo->getAccessor(rev, exportIgnore, "«" + input.to_string() + "»", smudgeLfs);
/* If the repo has submodules, fetch them and return a mounted
input accessor consisting of the accessor for the top-level
@@ -936,8 +781,8 @@ struct GitInputScheme : InputScheme
attrs.insert_or_assign("submodules", Explicit<bool>{true});
attrs.insert_or_assign("lfs", Explicit<bool>{smudgeLfs});
attrs.insert_or_assign("allRefs", Explicit<bool>{true});
auto submoduleInput = fetchers::Input::fromAttrs(settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(settings, store);
auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store);
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
mounts.insert_or_assign(submodule.path, submoduleAccessor);
}
@@ -954,7 +799,7 @@ struct GitInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessorFromWorkdir(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const
getAccessorFromWorkdir(ref<Store> store, RepoInfo & repoInfo, Input && input) const
{
auto repoPath = repoInfo.getPath().value();
@@ -968,7 +813,7 @@ struct GitInputScheme : InputScheme
auto exportIgnore = getExportIgnoreAttr(input);
ref<SourceAccessor> accessor =
repo->getAccessor(repoInfo.workdirInfo, {.exportIgnore = exportIgnore}, makeNotAllowedError(repoPath));
repo->getAccessor(repoInfo.workdirInfo, exportIgnore, makeNotAllowedError(repoPath));
/* If the repo has submodules, return a mounted input accessor
consisting of the accessor for the top-level repo and the
@@ -986,8 +831,8 @@ struct GitInputScheme : InputScheme
// TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out
// attrs.insert_or_assign("allRefs", Explicit<bool>{ true });
auto submoduleInput = fetchers::Input::fromAttrs(settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(settings, store);
auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store);
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
/* If the submodule is dirty, mark this repo dirty as
@@ -1014,12 +859,12 @@ struct GitInputScheme : InputScheme
input.attrs.insert_or_assign("rev", rev.gitRev());
if (!getShallowAttr(input)) {
input.attrs.insert_or_assign(
"revCount", rev == nullRev ? 0 : getRevCount(settings, repoInfo, repoPath, rev));
"revCount", rev == nullRev ? 0 : getRevCount(*input.settings, repoInfo, repoPath, rev));
}
verifyCommit(input, repo);
} else {
repoInfo.warnDirty(settings);
repoInfo.warnDirty(*input.settings);
if (repoInfo.workdirInfo.headRev) {
input.attrs.insert_or_assign("dirtyRev", repoInfo.workdirInfo.headRev->gitRev() + "-dirty");
@@ -1031,14 +876,14 @@ struct GitInputScheme : InputScheme
input.attrs.insert_or_assign(
"lastModified",
repoInfo.workdirInfo.headRev ? getLastModified(settings, repoInfo, repoPath, *repoInfo.workdirInfo.headRev)
: 0);
repoInfo.workdirInfo.headRev
? getLastModified(*input.settings, repoInfo, repoPath, *repoInfo.workdirInfo.headRev)
: 0);
return {accessor, std::move(input)};
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
Input input(_input);
@@ -1054,13 +899,13 @@ struct GitInputScheme : InputScheme
}
auto [accessor, final] = input.getRef() || input.getRev() || !repoInfo.getPath()
? getAccessorFromCommit(settings, store, repoInfo, std::move(input))
: getAccessorFromWorkdir(settings, store, repoInfo, std::move(input));
? getAccessorFromCommit(store, repoInfo, std::move(input))
: getAccessorFromWorkdir(store, repoInfo, std::move(input));
return {accessor, std::move(final)};
}
std::optional<std::string> getFingerprint(Store & store, const Input & input) const override
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override
{
auto makeFingerprint = [&](const Hash & rev) {
return rev.gitRev() + (getSubmodulesAttr(input) ? ";s" : "") + (getExportIgnoreAttr(input) ? ";e" : "")
@@ -1091,7 +936,7 @@ struct GitInputScheme : InputScheme
}
}
bool isLocked(const Settings & settings, const Input & input) const override
bool isLocked(const Input & input) const override
{
auto rev = input.getRev();
return rev && rev != nullRev;

View File

@@ -48,7 +48,7 @@ struct GitArchiveInputScheme : InputScheme
auto size = path.size();
if (size == 3) {
if (std::regex_match(path[2], revRegex))
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
rev = parseGitHash(path[2]);
else if (isLegalRefName(path[2]))
ref = path[2];
else
@@ -74,7 +74,7 @@ struct GitArchiveInputScheme : InputScheme
if (name == "rev") {
if (rev)
throw BadURL("URL '%s' contains multiple commit hashes", url);
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
rev = parseGitHash(value);
} else if (name == "ref") {
if (!isLegalRefName(value))
throw BadURL("URL '%s' contains an invalid branch/tag name", url);
@@ -92,7 +92,7 @@ struct GitArchiveInputScheme : InputScheme
if (ref && rev)
throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url, *ref, rev->gitRev());
Input input{};
Input input{settings};
input.attrs.insert_or_assign("type", std::string{schemeName()});
input.attrs.insert_or_assign("owner", path[0]);
input.attrs.insert_or_assign("repo", path[1]);
@@ -110,43 +110,18 @@ struct GitArchiveInputScheme : InputScheme
return input;
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
StringSet allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = {
{
"owner",
{},
},
{
"repo",
{},
},
{
"ref",
{},
},
{
"rev",
{},
},
{
"narHash",
{},
},
{
"lastModified",
{},
},
{
"host",
{},
},
{
"treeHash",
{},
},
return {
"owner",
"repo",
"ref",
"rev",
"narHash",
"lastModified",
"host",
"treeHash",
};
return attrs;
}
std::optional<Input> inputFromAttrs(const fetchers::Settings & settings, const Attrs & attrs) const override
@@ -154,7 +129,7 @@ struct GitArchiveInputScheme : InputScheme
getStrAttr(attrs, "owner");
getStrAttr(attrs, "repo");
Input input{};
Input input{settings};
input.attrs = attrs;
return input;
}
@@ -258,9 +233,9 @@ struct GitArchiveInputScheme : InputScheme
std::optional<Hash> treeHash;
};
virtual RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const = 0;
virtual RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const = 0;
virtual DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const = 0;
virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;
struct TarballInfo
{
@@ -268,7 +243,7 @@ struct GitArchiveInputScheme : InputScheme
time_t lastModified;
};
std::pair<Input, TarballInfo> downloadArchive(const Settings & settings, Store & store, Input input) const
std::pair<Input, TarballInfo> downloadArchive(ref<Store> store, Input input) const
{
if (!maybeGetStrAttr(input.attrs, "ref"))
input.attrs.insert_or_assign("ref", "HEAD");
@@ -277,7 +252,7 @@ struct GitArchiveInputScheme : InputScheme
auto rev = input.getRev();
if (!rev) {
auto refInfo = getRevFromRef(settings, store, input);
auto refInfo = getRevFromRef(store, input);
rev = refInfo.rev;
upstreamTreeHash = refInfo.treeHash;
debug("HEAD revision for '%s' is %s", input.to_string(), refInfo.rev.gitRev());
@@ -286,7 +261,7 @@ struct GitArchiveInputScheme : InputScheme
input.attrs.erase("ref");
input.attrs.insert_or_assign("rev", rev->gitRev());
auto cache = settings.getCache();
auto cache = input.settings->getCache();
Cache::Key treeHashKey{"gitRevToTreeHash", {{"rev", rev->gitRev()}}};
Cache::Key lastModifiedKey{"gitRevToLastModified", {{"rev", rev->gitRev()}}};
@@ -295,7 +270,7 @@ struct GitArchiveInputScheme : InputScheme
if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) {
auto treeHash = getRevAttr(*treeHashAttrs, "treeHash");
auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified");
if (settings.getTarballCache()->hasObject(treeHash))
if (getTarballCache()->hasObject(treeHash))
return {std::move(input), TarballInfo{.treeHash = treeHash, .lastModified = (time_t) lastModified}};
else
debug("Git tree with hash '%s' has disappeared from the cache, refetching...", treeHash.gitRev());
@@ -303,7 +278,7 @@ struct GitArchiveInputScheme : InputScheme
}
/* Stream the tarball into the tarball cache. */
auto url = getDownloadUrl(settings, input);
auto url = getDownloadUrl(input);
auto source = sinkToSource([&](Sink & sink) {
FileTransferRequest req(url.url);
@@ -315,7 +290,7 @@ struct GitArchiveInputScheme : InputScheme
*logger, lvlInfo, actUnknown, fmt("unpacking '%s' into the Git cache", input.to_string()));
TarArchive archive{*source};
auto tarballCache = settings.getTarballCache();
auto tarballCache = getTarballCache();
auto parseSink = tarballCache->getFileSystemObjectSink();
auto lastModified = unpackTarfileToSink(archive, *parseSink);
auto tree = parseSink->flush();
@@ -340,29 +315,28 @@ struct GitArchiveInputScheme : InputScheme
return {std::move(input), tarballInfo};
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
auto [input, tarballInfo] = downloadArchive(settings, store, _input);
auto [input, tarballInfo] = downloadArchive(store, _input);
#if 0
input.attrs.insert_or_assign("treeHash", tarballInfo.treeHash.gitRev());
#endif
input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified));
auto accessor =
settings.getTarballCache()->getAccessor(tarballInfo.treeHash, {}, "«" + input.to_string() + "»");
auto accessor = getTarballCache()->getAccessor(tarballInfo.treeHash, false, "«" + input.to_string() + "»");
return {accessor, input};
}
bool isLocked(const Settings & settings, const Input & input) const override
bool isLocked(const Input & input) const override
{
/* Since we can't verify the integrity of the tarball from the
Git revision alone, we also require a NAR hash for
locking. FIXME: in the future, we may want to require a Git
tree hash instead of a NAR hash. */
return input.getRev().has_value() && (settings.trustTarballsFromGitForges || input.getNarHash().has_value());
return input.getRev().has_value()
&& (input.settings->trustTarballsFromGitForges || input.getNarHash().has_value());
}
std::optional<ExperimentalFeature> experimentalFeature() const override
@@ -370,7 +344,7 @@ struct GitArchiveInputScheme : InputScheme
return Xp::Flakes;
}
std::optional<std::string> getFingerprint(Store & store, const Input & input) const override
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override
{
if (auto rev = input.getRev())
return rev->gitRev();
@@ -386,12 +360,6 @@ struct GitHubInputScheme : GitArchiveInputScheme
return "github";
}
std::string schemeDescription() const override
{
// TODO
return "";
}
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
{
// Github supports PAT/OAuth2 tokens and HTTP Basic
@@ -418,7 +386,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
return getStrAttr(input.attrs, "repo");
}
RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
{
auto host = getHost(input);
auto url = fmt(
@@ -428,22 +396,22 @@ struct GitHubInputScheme : GitArchiveInputScheme
getRepo(input),
*input.getRef());
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
auto downloadResult = downloadFile(store, settings, url, "source", headers);
auto downloadResult = downloadFile(store, *input.settings, url, "source", headers);
auto json = nlohmann::json::parse(
store.requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
return RefInfo{
.rev = Hash::parseAny(std::string{json["sha"]}, HashAlgorithm::SHA1),
.treeHash = Hash::parseAny(std::string{json["commit"]["tree"]["sha"]}, HashAlgorithm::SHA1)};
.rev = parseGitHash(std::string{json["sha"]}),
.treeHash = parseGitHash(std::string{json["commit"]["tree"]["sha"]})};
}
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
DownloadUrl getDownloadUrl(const Input & input) const override
{
auto host = getHost(input);
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
// If we have no auth headers then we default to the public archive
// urls so we do not run into rate limits.
@@ -457,13 +425,12 @@ struct GitHubInputScheme : GitArchiveInputScheme
return DownloadUrl{parseURL(url), headers};
}
void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
const override
void clone(const Input & input, const Path & destDir) const override
{
auto host = getHost(input);
Input::fromURL(settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input)))
Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input)))
.applyOverrides(input.getRef(), input.getRev())
.clone(settings, store, destDir);
.clone(destDir);
}
};
@@ -474,12 +441,6 @@ struct GitLabInputScheme : GitArchiveInputScheme
return "gitlab";
}
std::string schemeDescription() const override
{
// TODO
return "";
}
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
{
// Gitlab supports 4 kinds of authorization, two of which are
@@ -499,7 +460,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
return std::make_pair(token.substr(0, fldsplit), token.substr(fldsplit + 1));
}
RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// See rate limiting note below
@@ -510,14 +471,14 @@ struct GitLabInputScheme : GitArchiveInputScheme
getStrAttr(input.attrs, "repo"),
*input.getRef());
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
auto downloadResult = downloadFile(store, settings, url, "source", headers);
auto downloadResult = downloadFile(store, *input.settings, url, "source", headers);
auto json = nlohmann::json::parse(
store.requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) {
return RefInfo{.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)};
return RefInfo{.rev = parseGitHash(std::string(json[0]["id"]))};
}
if (json.is_array() && json.size() == 0) {
throw Error("No commits returned by GitLab API -- does the git ref really exist?");
@@ -526,7 +487,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
}
}
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
DownloadUrl getDownloadUrl(const Input & input) const override
{
// This endpoint has a rate limit threshold that may be
// server-specific and vary based whether the user is
@@ -541,20 +502,19 @@ struct GitLabInputScheme : GitArchiveInputScheme
getStrAttr(input.attrs, "repo"),
input.getRev()->to_string(HashFormat::Base16, false));
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
return DownloadUrl{parseURL(url), headers};
}
void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
const override
void clone(const Input & input, const Path & destDir) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// FIXME: get username somewhere
Input::fromURL(
settings,
*input.settings,
fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev())
.clone(settings, store, destDir);
.clone(destDir);
}
};
@@ -565,12 +525,6 @@ struct SourceHutInputScheme : GitArchiveInputScheme
return "sourcehut";
}
std::string schemeDescription() const override
{
// TODO
return "";
}
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
{
// SourceHut supports both PAT and OAuth2. See
@@ -581,7 +535,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
// Once it is implemented, however, should work as expected.
}
RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
{
// TODO: In the future, when the sourcehut graphql API is implemented for mercurial
// and with anonymous access, this method should use it instead.
@@ -592,12 +546,12 @@ struct SourceHutInputScheme : GitArchiveInputScheme
auto base_url =
fmt("https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"));
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
std::string refUri;
if (ref == "HEAD") {
auto downloadFileResult = downloadFile(store, settings, fmt("%s/HEAD", base_url), "source", headers);
auto contents = store.requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
auto downloadFileResult = downloadFile(store, *input.settings, fmt("%s/HEAD", base_url), "source", headers);
auto contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
auto remoteLine = git::parseLsRemoteLine(getLine(contents).first);
if (!remoteLine) {
@@ -609,8 +563,9 @@ struct SourceHutInputScheme : GitArchiveInputScheme
}
std::regex refRegex(refUri);
auto downloadFileResult = downloadFile(store, settings, fmt("%s/info/refs", base_url), "source", headers);
auto contents = store.requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
auto downloadFileResult =
downloadFile(store, *input.settings, fmt("%s/info/refs", base_url), "source", headers);
auto contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
std::istringstream is(contents);
std::string line;
@@ -624,10 +579,10 @@ struct SourceHutInputScheme : GitArchiveInputScheme
if (!id)
throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref);
return RefInfo{.rev = Hash::parseAny(*id, HashAlgorithm::SHA1)};
return RefInfo{.rev = parseGitHash(*id)};
}
DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override
DownloadUrl getDownloadUrl(const Input & input) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
auto url =
@@ -637,19 +592,18 @@ struct SourceHutInputScheme : GitArchiveInputScheme
getStrAttr(input.attrs, "repo"),
input.getRev()->to_string(HashFormat::Base16, false));
Headers headers = makeHeadersWithAuthTokens(settings, host, input);
Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input);
return DownloadUrl{parseURL(url), headers};
}
void clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir)
const override
void clone(const Input & input, const Path & destDir) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
Input::fromURL(
settings,
*input.settings,
fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev())
.clone(settings, store, destDir);
.clone(destDir);
}
};

View File

@@ -11,12 +11,6 @@
#include <sys/types.h>
namespace nix {
struct GitRepo;
}
namespace nix::fetchers {
struct Cache;
@@ -131,8 +125,6 @@ struct Settings : public Config
ref<Cache> getCache() const;
ref<GitRepo> getTarballCache() const;
private:
mutable Sync<std::shared_ptr<Cache>> _cache;
};

View File

@@ -36,6 +36,13 @@ struct Input
{
friend struct InputScheme;
const Settings * settings;
Input(const Settings & settings)
: settings{&settings}
{
}
std::shared_ptr<InputScheme> scheme; // note: can be null
Attrs attrs;
@@ -80,7 +87,7 @@ public:
* attributes like a Git revision or NAR hash that uniquely
* identify its contents.
*/
bool isLocked(const Settings & settings) const;
bool isLocked() const;
/**
* Only for relative path flakes, i.e. 'path:./foo', returns the
@@ -113,7 +120,7 @@ public:
* Fetch the entire input into the Nix store, returning the
* location in the Nix store and the locked input.
*/
std::pair<StorePath, Input> fetchToStore(const Settings & settings, Store & store) const;
std::pair<StorePath, Input> fetchToStore(ref<Store> store) const;
/**
* Check the locking attributes in `result` against
@@ -133,17 +140,17 @@ public:
* input without copying it to the store. Also return a possibly
* unlocked input.
*/
std::pair<ref<SourceAccessor>, Input> getAccessor(const Settings & settings, Store & store) const;
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store) const;
private:
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(const Settings & settings, Store & store) const;
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(ref<Store> store) const;
public:
Input applyOverrides(std::optional<std::string> ref, std::optional<Hash> rev) const;
void clone(const Settings & settings, Store & store, const std::filesystem::path & destDir) const;
void clone(const Path & destDir) const;
std::optional<std::filesystem::path> getSourcePath() const;
@@ -173,7 +180,7 @@ public:
*
* This is not a stable identifier between Nix versions, but not guaranteed to change either.
*/
std::optional<std::string> getFingerprint(Store & store) const;
std::optional<std::string> getFingerprint(ref<Store> store) const;
};
/**
@@ -203,34 +210,20 @@ struct InputScheme
*/
virtual std::string_view schemeName() const = 0;
/**
* Longform description of this scheme, for documentation purposes.
*/
virtual std::string schemeDescription() const = 0;
// TODO remove these defaults
struct AttributeInfo
{
const char * type = "String";
bool required = true;
const char * doc = "";
};
/**
* Allowed attributes in an attribute set that is converted to an
* input, and documentation for each attribute.
* input.
*
* `type` is not included from this map, because the `type` field is
* `type` is not included from this set, because the `type` field is
parsed first to choose which scheme; `type` is always required.
*/
virtual const std::map<std::string, AttributeInfo> & allowedAttrs() const = 0;
virtual StringSet allowedAttrs() const = 0;
virtual ParsedURL toURL(const Input & input) const;
virtual Input applyOverrides(const Input & input, std::optional<std::string> ref, std::optional<Hash> rev) const;
virtual void
clone(const Settings & settings, Store & store, const Input & input, const std::filesystem::path & destDir) const;
virtual void clone(const Input & input, const Path & destDir) const;
virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const;
@@ -240,8 +233,7 @@ struct InputScheme
std::string_view contents,
std::optional<std::string> commitMsg) const;
virtual std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & input) const = 0;
virtual std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & input) const = 0;
/**
* Is this `InputScheme` part of an experimental feature?
@@ -253,12 +245,12 @@ struct InputScheme
return true;
}
virtual std::optional<std::string> getFingerprint(Store & store, const Input & input) const
virtual std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const
{
return std::nullopt;
}
virtual bool isLocked(const Settings & settings, const Input & input) const
virtual bool isLocked(const Input & input) const
{
return false;
}
@@ -277,12 +269,7 @@ struct InputScheme
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
using InputSchemeMap = std::map<std::string_view, std::shared_ptr<InputScheme>>;
/**
* Use this for docs, not for finding a specific scheme
*/
const InputSchemeMap & getAllInputSchemes();
nlohmann::json dumpRegisterInputSchemeInfo();
struct PublicKey
{

View File

@@ -5,7 +5,7 @@
#include "nix/util/serialise.hh"
#include "nix/util/url.hh"
#include <git2/repository.h>
#include <git2-experimental/repository.h>
#include <nlohmann/json_fwd.hpp>

View File

@@ -22,12 +22,6 @@ struct GitFileSystemObjectSink : ExtendedFileSystemObjectSink
virtual Hash flush() = 0;
};
struct GitAccessorOptions
{
bool exportIgnore = false;
bool smudgeLfs = false;
};
struct GitRepo
{
virtual ~GitRepo() {}
@@ -95,10 +89,10 @@ struct GitRepo
virtual bool hasObject(const Hash & oid) = 0;
virtual ref<SourceAccessor>
getAccessor(const Hash & rev, const GitAccessorOptions & options, std::string displayPrefix) = 0;
getAccessor(const Hash & rev, bool exportIgnore, std::string displayPrefix, bool smudgeLfs = false) = 0;
virtual ref<SourceAccessor> getAccessor(
const WorkdirInfo & wd, const GitAccessorOptions & options, MakeNotAllowedError makeNotAllowedError) = 0;
virtual ref<SourceAccessor>
getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) = 0;
virtual ref<GitFileSystemObjectSink> getFileSystemObjectSink() = 0;
@@ -126,6 +120,8 @@ struct GitRepo
virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0;
};
ref<GitRepo> getTarballCache();
// A helper to ensure that the `git_*_free` functions get called.
template<auto del>
struct Deleter
@@ -171,4 +167,14 @@ struct Setter
*/
bool isLegalRefName(const std::string & refName);
/**
* Parse a base16-encoded git hash string and determine the hash
* algorithm based on the length (40 chars = SHA1, 64 chars = SHA256).
*
* @note For Nix-native information we should *not* do length tricks,
* but instead always rely on an explicit algorithm. This hack should be
* only for foreign hash literals.
*/
Hash parseGitHash(std::string_view hashStr);
} // namespace nix

View File

@@ -3,7 +3,6 @@
namespace nix::fetchers {
enum class UseRegistries : int;
struct Settings;
struct InputCache
{
@@ -15,8 +14,7 @@ struct InputCache
Attrs extraAttrs;
};
CachedResult
getAccessor(const Settings & settings, Store & store, const Input & originalInput, UseRegistries useRegistries);
CachedResult getAccessor(ref<Store> store, const Input & originalInput, UseRegistries useRegistries);
struct CachedInput
{

View File

@@ -13,6 +13,8 @@ namespace nix::fetchers {
struct Registry
{
const Settings & settings;
enum RegistryType {
Flag = 0,
User = 1,
@@ -32,8 +34,9 @@ struct Registry
std::vector<Entry> entries;
Registry(RegistryType type)
: type{type}
Registry(const Settings & settings, RegistryType type)
: settings{settings}
, type{type}
{
}
@@ -54,7 +57,7 @@ std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Pat
Path getUserRegistryPath();
Registries getRegistries(const Settings & settings, Store & store);
Registries getRegistries(const Settings & settings, ref<Store> store);
void overrideRegistry(const Input & from, const Input & to, const Attrs & extraAttrs);
@@ -68,7 +71,6 @@ enum class UseRegistries : int {
* Rewrite a flakeref using the registries. If `filter` is set, only
* use the registries for which the filter function returns true.
*/
std::pair<Input, Attrs>
lookupInRegistries(const Settings & settings, Store & store, const Input & input, UseRegistries useRegistries);
std::pair<Input, Attrs> lookupInRegistries(ref<Store> store, const Input & input, UseRegistries useRegistries);
} // namespace nix::fetchers

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