Compare commits

..

3 Commits

Author SHA1 Message Date
John Ericson
ddca102f8b Move StringData to its own header
Now that it isn't just used for `Value`, it doesn't really belong in
there.

Rename `static-string-data.hh` to share the same prefix to keep them
close together when sorting lines, also.
2025-11-18 14:01:32 -05:00
John Ericson
351e3870c9 Use StringData for Nix language value documentation too
This probably isn't much of a perf gain, but it does mean we are
completely rid of non-length prefixed strings. That allows us to delete
some code.
2025-11-18 14:01:32 -05:00
John Ericson
1ea6a71b0c Improve formatting of primop_fromTOML
Trailing comma helps a lot.
2025-11-18 14:01:32 -05:00
244 changed files with 2112 additions and 6457 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@7ec16f2c061ab07b235a7245e06ed46fe9a1cab6 # v31.8.3
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
./.

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

@@ -121,17 +121,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

@@ -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

@@ -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

@@ -130,19 +130,15 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
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)
lib.attrValues (finalAttrs.passthru.config.getComponents allComponents)
);
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)
lib.attrValues allComponents
++ lib.concatMap (c: lib.attrValues c.tests or { }) (lib.attrValues allComponents)
);
isInternal =
@@ -191,19 +187,19 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
);
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
(finalAttrs.finalPackage.withActiveComponents (c: {
inherit (c)
nix-cli
nix-util-tests
nix-store-tests
nix-expr-tests
nix-fetchers-tests
nix-flake-tests
nix-functional-tests
# Currently required
nix-perl-bindings
;
})).overrideAttrs
(o: {
mesonFlags = o.mesonFlags ++ [
# TODO: infer from activeComponents or vice versa
@@ -273,8 +269,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)

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,7 +29,6 @@ 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
./.
];

View File

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

View File

@@ -65,6 +65,6 @@ mkMesonDerivation (finalAttrs: {
'';
meta = {
platforms = lib.platforms.unix;
platforms = lib.platforms.all;
};
})

View File

@@ -297,7 +297,7 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
MixDefaultProfile::MixDefaultProfile()
{
profile = getDefaultProfile().string();
profile = getDefaultProfile();
}
MixEnvironment::MixEnvironment()
@@ -391,7 +391,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
auto symlink = outLink;
if (i)
symlink += fmt("-%d", i);
store.addPermRoot(bo.path, absPath(symlink).string());
store.addPermRoot(bo.path, absPath(symlink.string()));
},
[&](const BuiltPath::Built & bfd) {
for (auto & output : bfd.outputs) {
@@ -400,7 +400,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
symlink += fmt("-%d", i);
if (output.first != "out")
symlink += fmt("-%s", output.first);
store.addPermRoot(output.second, absPath(symlink).string());
store.addPermRoot(output.second, absPath(symlink.string()));
}
},
},

View File

@@ -34,7 +34,7 @@ EvalSettings evalSettings{
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);
flakeRef.resolve(fetchSettings, state.store).lazyFetch(fetchSettings, state.store);
auto storePath = nix::fetchToStore(
state.fetchSettings,
*state.store,
@@ -132,7 +132,7 @@ MixEvalArgs::MixEvalArgs()
fetchers::Attrs extraAttrs;
if (to.subdir != "")
extraAttrs["dir"] = to.subdir;
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
fetchers::overrideRegistry(fetchSettings, from.input, to.input, extraAttrs);
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, openStore(), prefix);
@@ -165,22 +165,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 +188,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(fetchSettings, state.store).lazyFetch(fetchSettings, state.store);
auto storePath = nix::fetchToStore(
state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
state.allowPath(storePath);
@@ -197,13 +196,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();
@@ -310,7 +310,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

@@ -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
@@ -185,6 +185,7 @@ MixFlakeOptions::MixFlakeOptions()
}
overrideRegistry(
fetchSettings,
fetchers::Input::fromAttrs(fetchSettings, {{"type", "indirect"}, {"id", inputName}}),
input3->lockedRef.input,
extraAttrs);
@@ -263,7 +264,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 +410,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 +466,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 +802,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 +821,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({
@@ -657,7 +656,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
+ "\n\n";
}
markdown += stripIndentation(doc->doc);
markdown += stripIndentation(doc->doc.view());
logger->cout(trim(renderMarkdownToTerminal(markdown)));
} else if (fallbackPos) {
@@ -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);
}
@@ -740,7 +739,7 @@ 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);
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
}

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 */
@@ -152,8 +134,8 @@ PrimOp * nix_alloc_primop(
.name = name,
.args = {},
.arity = (size_t) arity,
.doc = doc,
.fun = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)};
.doc = &nix::StringData::make(doc),
.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

@@ -1,6 +1,6 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/value-to-json.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"
namespace nix {
// Testing the conversion to JSON
@@ -73,7 +73,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

@@ -1,5 +1,5 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/expr/value.hh"
#include "nix/expr/print.hh"
@@ -268,7 +268,7 @@ struct StringPrintingTests : LibExprTest
void test(std::string_view literal, std::string_view expected, unsigned int maxLength, A... args)
{
Value v;
v.mkString(literal, state.mem);
v.mkString(literal);
std::stringstream out;
printValue(state, out, v, PrintOptions{.maxStringLength = maxLength});
@@ -353,7 +353,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});
}

View File

@@ -1,5 +1,5 @@
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/tests/libstore.hh"
#include <gtest/gtest.h>

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

@@ -51,50 +51,20 @@ using json = nlohmann::json;
namespace nix {
/**
* Just for doc strings. Not for regular string values.
*/
static char * allocString(size_t size)
StringData & StringData::alloc(size_t size)
{
char * t;
t = (char *) GC_MALLOC_ATOMIC(size);
if (!t)
throw std::bad_alloc();
return t;
}
// When there's no need to write to the string, we can optimize away empty
// 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();
if (size == 0)
return "";
auto t = allocString(size + 1);
memcpy(t, s.data(), size);
t[size] = '\0';
return t;
}
StringData & StringData::alloc(EvalMemory & mem, size_t size)
{
void * t = mem.allocBytes(sizeof(StringData) + size + 1);
void * t = GC_MALLOC_ATOMIC(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)
const StringData & StringData::make(std::string_view s)
{
if (s.empty())
return ""_sds;
auto & res = alloc(mem, s.size());
auto & res = alloc(s.size());
std::memcpy(&res.data_, s.data(), s.size());
res.data_[s.size()] = '\0';
return res;
@@ -205,7 +175,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 +487,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 +535,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 +583,8 @@ 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()),
/* NOTE: memory leak when compiled without GC. */
.doc = StringData::make(s.view()),
};
}
if (isFunctor(v)) {
@@ -849,9 +816,9 @@ 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(StringData::make(s));
}
Value::StringWithContext::Context *
@@ -862,13 +829,13 @@ Value::StringWithContext::Context::fromBuilder(const NixStringContext & context,
auto ctx = new (mem.allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
std::ranges::transform(
context, ctx->elems, [&](const NixStringContextElem & elt) { return &StringData::make(mem, elt.to_string()); });
context, ctx->elems, [](const NixStringContextElem & elt) { return &StringData::make(elt.to_string()); });
return ctx;
}
void Value::mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem)
{
mkStringNoCopy(StringData::make(mem, s), Value::StringWithContext::Context::fromBuilder(context, mem));
mkStringNoCopy(StringData::make(s), Value::StringWithContext::Context::fromBuilder(context, mem));
}
void Value::mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem)
@@ -876,9 +843,9 @@ void Value::mkStringMove(const StringData & s, const NixStringContext & context,
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context, mem));
}
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, StringData::make(path.path.abs()));
}
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
@@ -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
@@ -1226,26 +1193,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 +1239,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 +1252,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 +1292,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 +1301,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 +1718,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);
}
@@ -2139,9 +2106,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
for (const auto & part : strings) {
resultStr += *part;
}
v.mkPath(state.rootPath(CanonPath(resultStr)), state.mem);
v.mkPath(state.rootPath(CanonPath(resultStr)));
} else {
auto & resultStr = StringData::alloc(state.mem, sSize);
auto & resultStr = StringData::alloc(sSize);
auto * tmp = resultStr.data();
for (const auto & part : strings) {
std::memcpy(tmp, part->data(), part->size());
@@ -2188,8 +2155,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 +2181,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);
}
@@ -3199,7 +3157,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

@@ -108,7 +108,7 @@ struct PrimOp
/**
* Optional free-form documentation about the primop.
*/
std::optional<std::string> doc;
const StringData * doc = nullptr;
/**
* Add a trace item, while calling the `<name>` builtin.
@@ -156,7 +156,7 @@ struct Constant
/**
* Optional free-form documentation about the constant.
*/
const char * doc = nullptr;
const StringData * doc = nullptr;
/**
* Whether the constant is impure, and not available in pure mode.
@@ -837,11 +837,7 @@ public:
std::optional<std::string> name;
size_t arity;
std::vector<std::string> args;
/**
* Unlike the other `doc` fields in this file, this one should never be
* `null`.
*/
const char * doc;
const StringData & doc;
};
/**

View File

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

View File

@@ -12,7 +12,7 @@
#include "nix/expr/value.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/eval-error.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/util/pos-idx.hh"
#include "nix/expr/counter.hh"
#include "nix/util/pos-table.hh"
@@ -395,13 +395,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 +409,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,7 +427,6 @@ 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
@@ -593,14 +581,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 +593,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 +608,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
};
@@ -841,7 +825,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 +833,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));

View File

@@ -5,7 +5,7 @@
#include "nix/expr/eval.hh"
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"
namespace nix {
@@ -126,8 +126,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());
@@ -135,11 +135,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;
}
@@ -148,7 +148,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);
@@ -165,8 +165,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.
@@ -181,8 +181,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);
@@ -192,12 +192,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(),
@@ -210,7 +210,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);
}
}

View File

@@ -1,7 +1,7 @@
#pragma once
///@file
#include "nix/expr/value.hh"
#include "nix/expr/string-data.hh"
namespace nix {

View File

@@ -0,0 +1,96 @@
#pragma once
///@file
#include <cstddef>
#include <cstring>
#include <memory_resource>
#include <string_view>
namespace nix {
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(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(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 nix

View File

@@ -3,7 +3,7 @@
#include <memory_resource>
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/util/chunked-vector.hh"
#include "nix/util/error.hh"

View File

@@ -3,16 +3,14 @@
#include <bit>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <memory>
#include <memory_resource>
#include <span>
#include <string_view>
#include <type_traits>
#include <concepts>
#include "nix/expr/eval-gc.hh"
#include "nix/expr/string-data.hh"
#include "nix/expr/value/context.hh"
#include "nix/util/source-path.hh"
#include "nix/expr/print-options.hh"
@@ -193,91 +191,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 {
/**
@@ -1147,13 +1060,13 @@ public:
setStorage(StringWithContext{.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 mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem);
void mkPath(const SourcePath & path, EvalMemory & mem);
void mkPath(const SourcePath & path);
inline void mkPath(SourceAccessor * accessor, const StringData & path) 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

@@ -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});
@@ -129,7 +129,7 @@ static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
%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 <std::vector<Expr *>> list
%type <ExprAttrs *> binds binds1
%type <FormalsBuilder> formals formal_set
%type <Formal> formal
@@ -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]
@@ -413,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));
}
@@ -423,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);
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),

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
#include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/derivations.hh"
#include "nix/store/store-api.hh"
#include "nix/store/globals.hh"
@@ -11,15 +12,15 @@ 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({
.name = "__unsafeDiscardStringContext",
.args = {"s"},
.doc = R"(
.doc = &R"(
Discard the [string context](@docroot@/language/string-context.md) from a value that can be coerced to a string.
)",
)"_sds,
.fun = prim_unsafeDiscardStringContext,
});
@@ -33,7 +34,7 @@ static void prim_hasContext(EvalState & state, const PosIdx pos, Value ** args,
static RegisterPrimOp primop_hasContext(
{.name = "__hasContext",
.args = {"s"},
.doc = R"(
.doc = &R"(
Return `true` if string *s* has a non-empty context.
The context can be obtained with
[`getContext`](#builtins-getContext).
@@ -50,7 +51,7 @@ static RegisterPrimOp primop_hasContext(
> then throw "package name cannot contain string context"
> else { ${name} = meta; }
> ```
)",
)"_sds,
.fun = prim_hasContext});
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -75,7 +76,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
static RegisterPrimOp primop_unsafeDiscardOutputDependency(
{.name = "__unsafeDiscardOutputDependency",
.args = {"s"},
.doc = R"(
.doc = &R"(
Create a copy of the given string where every
[derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep)
string context element is turned into a
@@ -91,7 +92,7 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency(
Replacing a constant string context element with a "derivation deep" element is a safe operation that just enlargens the string context without forgetting anything.
[`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies
)",
)"_sds,
.fun = prim_unsafeDiscardOutputDependency});
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -143,7 +144,7 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V
static RegisterPrimOp primop_addDrvOutputDependencies(
{.name = "__addDrvOutputDependencies",
.args = {"s"},
.doc = R"(
.doc = &R"(
Create a copy of the given string where a single
[constant](@docroot@/language/string-context.md#string-context-constant)
string context element is turned into a
@@ -156,7 +157,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies(
The latter is supported so this function is idempotent.
This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency).
)",
)"_sds,
.fun = prim_addDrvOutputDependencies});
/* Extract the context of a string as a structured Nix value.
@@ -218,7 +219,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);
@@ -230,7 +231,7 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args,
static RegisterPrimOp primop_getContext(
{.name = "__getContext",
.args = {"s"},
.doc = R"(
.doc = &R"(
Return the string context of *s*.
The string context tracks references to derivations within a string.
@@ -248,7 +249,7 @@ static RegisterPrimOp primop_getContext(
```
{ "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; }
```
)",
)"_sds,
.fun = prim_getContext});
/* Append the given context to a given string.

View File

@@ -1,4 +1,5 @@
#include "nix/expr/primops.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/store-open.hh"
#include "nix/store/realisation.hh"
#include "nix/store/make-content-addressed.hh"
@@ -216,7 +217,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value ** args
static RegisterPrimOp primop_fetchClosure({
.name = "__fetchClosure",
.args = {"args"},
.doc = R"(
.doc = &R"(
Fetch a store path [closure](@docroot@/glossary.md#gloss-closure) from a binary cache, and return the store path as a string with context.
This function can be invoked in three ways that we will discuss in order of preference.
@@ -284,7 +285,7 @@ static RegisterPrimOp primop_fetchClosure({
`fetchClosure` is similar to [`builtins.storePath`](#builtins-storePath) in that it allows you to use a previously built store path in a Nix expression.
However, `fetchClosure` is more reproducible because it specifies a binary cache from which the path can be fetched.
Also, using content-addressed store paths does not require users to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity.
)",
)"_sds,
.fun = prim_fetchClosure,
.experimentalFeature = Xp::FetchClosure,
});

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.fetchSettings, state.store);
auto attrs2 = state.buildBindings(8);
state.mkStorePathString(storePath, attrs2.alloc(state.s.outPath));
if (input2.getRef())
attrs2.alloc("branch").mkString(*input2.getRef(), 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

@@ -2,6 +2,7 @@
#include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/store/store-api.hh"
#include "nix/fetchers/fetchers.hh"
#include "nix/store/filetransfer.hh"
@@ -35,7 +36,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 +44,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 +60,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);
@@ -195,7 +195,7 @@ 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.fetchSettings, state.store, input, fetchers::UseRegistries::Limited).first;
if (state.settings.pureEval && !input.isLocked(state.fetchSettings)) {
if (input.getNarHash())
@@ -221,7 +221,7 @@ static void fetchTree(
}
auto cachedInput =
state.inputCache->getAccessor(state.fetchSettings, *state.store, input, fetchers::UseRegistries::No);
state.inputCache->getAccessor(state.fetchSettings, state.store, input, fetchers::UseRegistries::No);
auto storePath = state.mountInput(cachedInput.lockedInput, input, cachedInput.accessor);
@@ -236,127 +236,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";
}
}
- `"git"`
doc += "\n" + stripIndentation(R"(
The following input types are still subject to change:
Fetch a Git tree and copy it to the Nix store.
This is similar to [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit).
- `"path"`
- `"github"`
- `"gitlab"`
- `"sourcehut"`
- `"mercurial"`
- `allRefs` (Bool, optional)
*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.
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.
- `lfs` (Bool, optional)
Fetch any [Git LFS](https://git-lfs.com/) files.
Default: `false`
- `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`
- `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.
- `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`
- `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;
}(),
- `"tarball"`
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)
- `url` (String, required)
> **Example**
>
> ```nix
> fetchTree {
> type = "tarball";
> url = "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-23.11";
> }
> ```
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"
> ```
)"_sds,
.fun = prim_fetchTree,
.experimentalFeature = Xp::FetchTree,
});
@@ -481,10 +583,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
@@ -516,7 +618,7 @@ static void prim_fetchurl(EvalState & state, const PosIdx pos, Value ** args, Va
static RegisterPrimOp primop_fetchurl({
.name = "__fetchurl",
.args = {"arg"},
.doc = R"(
.doc = &R"(
Download the specified URL and return the path of the downloaded file.
`arg` can be either a string denoting the URL, or an attribute set with the following attributes:
@@ -530,7 +632,7 @@ static RegisterPrimOp primop_fetchurl({
characters that are invalid for the store.
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)",
)"_sds,
.fun = prim_fetchurl,
});
@@ -542,7 +644,7 @@ static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value ** args
static RegisterPrimOp primop_fetchTarball({
.name = "fetchTarball",
.args = {"args"},
.doc = R"(
.doc = &R"(
Download the specified URL, unpack it and return the path of the
unpacked tree. The file must be a tape archive (`.tar`) compressed
with `gzip`, `bzip2` or `xz`. If the tarball consists of a
@@ -580,7 +682,7 @@ static RegisterPrimOp primop_fetchTarball({
```
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)",
)"_sds,
.fun = prim_fetchTarball,
});
@@ -593,7 +695,7 @@ static void prim_fetchGit(EvalState & state, const PosIdx pos, Value ** args, Va
static RegisterPrimOp primop_fetchGit({
.name = "fetchGit",
.args = {"args"},
.doc = R"(
.doc = &R"(
Fetch a path from git. *args* can be a URL, in which case the HEAD
of the repo at that URL is fetched. Otherwise, it can be an
attribute with the following attributes (all except `url` optional):
@@ -796,7 +898,7 @@ static RegisterPrimOp primop_fetchGit({
given, `fetchGit` uses the current content of the checked-out
files, even if they are not committed or added to Git's index. It
only considers files added to the Git repository, as listed by `git ls-files`.
)",
)"_sds,
.fun = prim_fetchGit,
});

View File

@@ -1,6 +1,6 @@
#include "nix/expr/primops.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"
#include "expr-config-private.hh"
@@ -126,7 +126,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
case toml::value_t::string: {
auto s = toml::get<std::string_view>(t);
forceNoNullByte(s);
v.mkString(s, state.mem);
v.mkString(s);
} break;
case toml::value_t::local_datetime:
case toml::value_t::offset_datetime:
@@ -142,7 +142,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
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");
@@ -170,10 +170,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
}
}
static RegisterPrimOp primop_fromTOML(
{.name = "fromTOML",
.args = {"e"},
.doc = R"(
static RegisterPrimOp primop_fromTOML({
.name = "fromTOML",
.args = {"e"},
.doc = &R"(
Convert a TOML string to a Nix value. For example,
```nix
@@ -186,7 +186,8 @@ static RegisterPrimOp primop_fromTOML(
```
returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
)",
.fun = prim_fromTOML});
)"_sds,
.fun = prim_fromTOML,
});
} // namespace nix

View File

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

View File

@@ -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

@@ -196,7 +196,7 @@ TEST_F(GitTest, submodulePeriodSupport)
{"ref", "main"},
});
auto [accessor, i] = input.getAccessor(settings, *store);
auto [accessor, i] = input.getAccessor(settings, store);
ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n");
}

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)
@@ -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;
@@ -190,7 +198,7 @@ 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(const Settings & settings, ref<Store> store) const
{
if (!scheme)
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
@@ -200,9 +208,9 @@ std::pair<StorePath, Input> Input::fetchToStore(const Settings & settings, Store
auto [accessor, result] = getAccessorUnchecked(settings, store);
auto storePath =
nix::fetchToStore(settings, store, SourcePath(accessor), FetchMode::Copy, result.getName());
nix::fetchToStore(settings, *store, SourcePath(accessor), FetchMode::Copy, result.getName());
auto narHash = store.queryPathInfo(storePath)->narHash;
auto narHash = store->queryPathInfo(storePath)->narHash;
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
@@ -289,7 +297,7 @@ 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(const Settings & settings, ref<Store> store) const
{
try {
auto [accessor, result] = getAccessorUnchecked(settings, store);
@@ -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(const Settings & settings, 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() + "»");
@@ -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 Settings & settings, const Path & destDir) const
{
assert(scheme);
scheme->clone(settings, store, *this, destDir);
scheme->clone(settings, *this, destDir);
}
std::optional<std::filesystem::path> Input::getSourcePath() const
@@ -485,19 +493,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 Settings & settings, 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

@@ -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

@@ -541,15 +541,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 +668,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 +716,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 +1254,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 +1283,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 +1299,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 +1316,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
@@ -1334,8 +1332,10 @@ namespace fetchers {
ref<GitRepo> Settings::getTarballCache() const
{
static auto repoDir = std::filesystem::path(getCacheDir()) / "tarball-cache";
return GitRepo::openRepo(repoDir, true, true);
auto tarballCache(_tarballCache.lock());
if (!*tarballCache)
*tarballCache = GitRepo::openRepo(std::filesystem::path(getCacheDir()) / "tarball-cache", true, true);
return ref<GitRepo>(*tarballCache);
}
} // namespace fetchers

View File

@@ -194,183 +194,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
@@ -433,8 +278,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 Settings & settings, const Input & input, const Path & destDir) const override
{
auto repoInfo = getRepoInfo(input);
@@ -779,7 +623,7 @@ struct GitInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessorFromCommit(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const
getAccessorFromCommit(const Settings & settings, ref<Store> store, RepoInfo & repoInfo, Input && input) const
{
assert(!repoInfo.workdirInfo.isDirty);
@@ -900,8 +744,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
@@ -954,7 +797,7 @@ struct GitInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessorFromWorkdir(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const
getAccessorFromWorkdir(const Settings & settings, ref<Store> store, RepoInfo & repoInfo, Input && input) const
{
auto repoPath = repoInfo.getPath().value();
@@ -968,7 +811,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
@@ -1038,7 +881,7 @@ struct GitInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
{
Input input(_input);
@@ -1060,7 +903,7 @@ struct GitInputScheme : InputScheme
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" : "")

View File

@@ -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
@@ -258,7 +233,7 @@ struct GitArchiveInputScheme : InputScheme
std::optional<Hash> treeHash;
};
virtual RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const = 0;
virtual RefInfo getRevFromRef(const Settings & settings, nix::ref<Store> store, const Input & input) const = 0;
virtual DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const = 0;
@@ -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(const Settings & settings, ref<Store> store, Input input) const
{
if (!maybeGetStrAttr(input.attrs, "ref"))
input.attrs.insert_or_assign("ref", "HEAD");
@@ -341,7 +316,7 @@ struct GitArchiveInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
{
auto [input, tarballInfo] = downloadArchive(settings, store, _input);
@@ -351,7 +326,7 @@ struct GitArchiveInputScheme : InputScheme
input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified));
auto accessor =
settings.getTarballCache()->getAccessor(tarballInfo.treeHash, {}, "«" + input.to_string() + "»");
settings.getTarballCache()->getAccessor(tarballInfo.treeHash, false, "«" + input.to_string() + "»");
return {accessor, input};
}
@@ -370,7 +345,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 +361,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 +387,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
return getStrAttr(input.attrs, "repo");
}
RefInfo getRevFromRef(const Settings & settings, nix::Store & store, const Input & input) const override
RefInfo getRevFromRef(const Settings & settings, nix::ref<Store> store, const Input & input) const override
{
auto host = getHost(input);
auto url = fmt(
@@ -432,7 +401,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
auto downloadResult = downloadFile(store, settings, url, "source", headers);
auto json = nlohmann::json::parse(
store.requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
return RefInfo{
.rev = Hash::parseAny(std::string{json["sha"]}, HashAlgorithm::SHA1),
@@ -457,13 +426,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 Settings & settings, 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)))
.applyOverrides(input.getRef(), input.getRev())
.clone(settings, store, destDir);
.clone(settings, destDir);
}
};
@@ -474,12 +442,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 +461,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(const Settings & settings, nix::ref<Store> store, const Input & input) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// See rate limiting note below
@@ -514,7 +476,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
auto downloadResult = downloadFile(store, settings, url, "source", headers);
auto json = nlohmann::json::parse(
store.requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) {
return RefInfo{.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)};
@@ -545,8 +507,7 @@ struct GitLabInputScheme : 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 Settings & settings, const Input & input, const Path & destDir) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// FIXME: get username somewhere
@@ -554,7 +515,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
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(settings, destDir);
}
};
@@ -565,12 +526,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 +536,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(const Settings & settings, 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.
@@ -597,7 +552,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
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 contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
auto remoteLine = git::parseLsRemoteLine(getLine(contents).first);
if (!remoteLine) {
@@ -610,7 +565,7 @@ 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 contents = store->requireStoreObjectAccessor(downloadFileResult.storePath)->readFile(CanonPath::root);
std::istringstream is(contents);
std::string line;
@@ -641,15 +596,14 @@ struct SourceHutInputScheme : 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 Settings & settings, const Input & input, const Path & destDir) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
Input::fromURL(
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(settings, destDir);
}
};

View File

@@ -135,6 +135,8 @@ struct Settings : public Config
private:
mutable Sync<std::shared_ptr<Cache>> _cache;
mutable Sync<std::shared_ptr<GitRepo>> _tarballCache;
};
} // namespace nix::fetchers

View File

@@ -113,7 +113,7 @@ public:
* Fetch the entire input into the Nix store, returning the
* location in the Nix store and the locked input.
*/
std::pair<StorePath, Input> fetchToStore(const Settings & settings, Store & store) const;
std::pair<StorePath, Input> fetchToStore(const Settings & settings, ref<Store> store) const;
/**
* Check the locking attributes in `result` against
@@ -133,17 +133,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(const Settings & settings, ref<Store> store) const;
private:
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(const Settings & settings, Store & store) const;
std::pair<ref<SourceAccessor>, Input> getAccessorUnchecked(const Settings & settings, 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 Settings & settings, const Path & destDir) const;
std::optional<std::filesystem::path> getSourcePath() const;
@@ -173,7 +173,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 +203,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 Settings & settings, const Input & input, const Path & destDir) const;
virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const;
@@ -241,7 +227,7 @@ struct InputScheme
std::optional<std::string> commitMsg) const;
virtual std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & input) const = 0;
getAccessor(const Settings & settings, ref<Store> store, const Input & input) const = 0;
/**
* Is this `InputScheme` part of an experimental feature?
@@ -253,7 +239,7 @@ 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;
}
@@ -277,12 +263,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

@@ -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;

View File

@@ -16,7 +16,7 @@ struct InputCache
};
CachedResult
getAccessor(const Settings & settings, Store & store, const Input & originalInput, UseRegistries useRegistries);
getAccessor(const Settings & settings, 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,9 +57,9 @@ 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);
void overrideRegistry(const Settings & settings, const Input & from, const Input & to, const Attrs & extraAttrs);
enum class UseRegistries : int {
No,
@@ -69,6 +72,6 @@ enum class UseRegistries : int {
* 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);
lookupInRegistries(const Settings & settings, ref<Store> store, const Input & input, UseRegistries useRegistries);
} // namespace nix::fetchers

View File

@@ -25,7 +25,7 @@ struct DownloadFileResult
};
DownloadFileResult downloadFile(
Store & store,
ref<Store> store,
const Settings & settings,
const std::string & url,
const std::string & name,
@@ -43,6 +43,6 @@ struct DownloadTarballResult
* Download and import a tarball into the Git cache. The result is the
* Git tree hash of the root directory.
*/
ref<SourceAccessor> downloadTarball(Store & store, const Settings & settings, const std::string & url);
ref<SourceAccessor> downloadTarball(ref<Store> store, const Settings & settings, const std::string & url);
} // namespace nix::fetchers

View File

@@ -60,33 +60,14 @@ struct IndirectInputScheme : InputScheme
return "indirect";
}
std::string schemeDescription() const override
StringSet allowedAttrs() const override
{
// TODO
return "";
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = {
{
"id",
{},
},
{
"ref",
{},
},
{
"rev",
{},
},
{
"narHash",
{},
},
return {
"id",
"ref",
"rev",
"narHash",
};
return attrs;
}
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -126,7 +107,7 @@ struct IndirectInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & input) const override
getAccessor(const Settings & settings, ref<Store> store, const Input & input) const override
{
throw Error("indirect input '%s' cannot be fetched directly", input.to_string());
}

View File

@@ -6,7 +6,7 @@
namespace nix::fetchers {
InputCache::CachedResult InputCache::getAccessor(
const Settings & settings, Store & store, const Input & originalInput, UseRegistries useRegistries)
const Settings & settings, ref<Store> store, const Input & originalInput, UseRegistries useRegistries)
{
auto fetched = lookup(originalInput);
Input resolvedInput = originalInput;

View File

@@ -68,41 +68,16 @@ struct MercurialInputScheme : InputScheme
return "hg";
}
std::string schemeDescription() const override
StringSet allowedAttrs() const override
{
// TODO
return "";
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = {
{
"url",
{},
},
{
"ref",
{},
},
{
"rev",
{},
},
{
"revCount",
{},
},
{
"narHash",
{},
},
{
"name",
{},
},
return {
"url",
"ref",
"rev",
"revCount",
"narHash",
"name",
};
return attrs;
}
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -179,7 +154,7 @@ struct MercurialInputScheme : InputScheme
return {isLocal, isLocal ? renderUrlPathEnsureLegal(url.path) : url.to_string()};
}
StorePath fetchToStore(const Settings & settings, Store & store, Input & input) const
StorePath fetchToStore(const Settings & settings, ref<Store> store, Input & input) const
{
auto origRev = input.getRev();
@@ -230,7 +205,7 @@ struct MercurialInputScheme : InputScheme
return files.count(file);
};
auto storePath = store.addToStore(
auto storePath = store->addToStore(
input.getName(),
{getFSSourceAccessor(), CanonPath(actualPath)},
ContentAddressMethod::Raw::NixArchive,
@@ -251,7 +226,7 @@ struct MercurialInputScheme : InputScheme
"Hash '%s' is not supported by Mercurial. Only sha1 is supported.",
rev.to_string(HashFormat::Base16, true));
return Cache::Key{"hgRev", {{"store", store.storeDir}, {"name", name}, {"rev", input.getRev()->gitRev()}}};
return Cache::Key{"hgRev", {{"store", store->storeDir}, {"name", name}, {"rev", input.getRev()->gitRev()}}};
};
auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath {
@@ -271,7 +246,7 @@ struct MercurialInputScheme : InputScheme
/* If we have a rev, check if we have a cached store path. */
if (auto rev = input.getRev()) {
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(*rev), store))
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(*rev), *store))
return makeResult(res->value, res->storePath);
}
@@ -325,7 +300,7 @@ struct MercurialInputScheme : InputScheme
/* Now that we have the rev, check the cache again for a
cached store path. */
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(rev), store))
if (auto res = settings.getCache()->lookupStorePath(revInfoKey(rev), *store))
return makeResult(res->value, res->storePath);
Path tmpDir = createTempDir();
@@ -335,7 +310,7 @@ struct MercurialInputScheme : InputScheme
deletePath(tmpDir + "/.hg_archival.txt");
auto storePath = store.addToStore(name, {getFSSourceAccessor(), CanonPath(tmpDir)});
auto storePath = store->addToStore(name, {getFSSourceAccessor(), CanonPath(tmpDir)});
Attrs infoAttrs({
{"revCount", (uint64_t) revCount},
@@ -344,18 +319,18 @@ struct MercurialInputScheme : InputScheme
if (!origRev)
settings.getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}});
settings.getCache()->upsert(revInfoKey(rev), store, infoAttrs, storePath);
settings.getCache()->upsert(revInfoKey(rev), *store, infoAttrs, storePath);
return makeResult(infoAttrs, std::move(storePath));
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
{
Input input(_input);
auto storePath = fetchToStore(settings, store, input);
auto accessor = store.requireStoreObjectAccessor(storePath);
auto accessor = store->requireStoreObjectAccessor(storePath);
accessor->setPathDisplay("«" + input.to_string() + "»");
@@ -367,7 +342,7 @@ struct MercurialInputScheme : InputScheme
return (bool) input.getRev();
}
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();

View File

@@ -40,42 +40,20 @@ struct PathInputScheme : InputScheme
return "path";
}
std::string schemeDescription() const override
StringSet allowedAttrs() const override
{
// TODO
return "";
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = {
{
"path",
{},
},
return {
"path",
/* Allow the user to pass in "fake" tree info
attributes. This is useful for making a pinned tree work
the same as the repository from which is exported (e.g.
path:/nix/store/...-source?lastModified=1585388205&rev=b0c285...).
*/
{
"rev",
{},
},
{
"revCount",
{},
},
{
"lastModified",
{},
},
{
"narHash",
{},
},
"rev",
"revCount",
"lastModified",
"narHash",
};
return attrs;
}
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -139,7 +117,7 @@ struct PathInputScheme : InputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
{
Input input(_input);
auto path = getStrAttr(input.attrs, "path");
@@ -147,31 +125,31 @@ struct PathInputScheme : InputScheme
auto absPath = getAbsPath(input);
// FIXME: check whether access to 'path' is allowed.
auto storePath = store.maybeParseStorePath(absPath.string());
auto storePath = store->maybeParseStorePath(absPath.string());
if (storePath)
store.addTempRoot(*storePath);
store->addTempRoot(*storePath);
time_t mtime = 0;
if (!storePath || storePath->name() != "source" || !store.isValidPath(*storePath)) {
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) {
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying %s to the store", absPath));
// FIXME: try to substitute storePath.
auto src = sinkToSource(
[&](Sink & sink) { mtime = dumpPathAndGetMtime(absPath.string(), sink, defaultPathFilter); });
storePath = store.addToStoreFromDump(*src, "source");
storePath = store->addToStoreFromDump(*src, "source");
}
auto accessor = store.requireStoreObjectAccessor(*storePath);
auto accessor = store->requireStoreObjectAccessor(*storePath);
// To prevent `fetchToStore()` copying the path again to Nix
// store, pre-create an entry in the fetcher cache.
auto info = store.queryPathInfo(*storePath);
auto info = store->queryPathInfo(*storePath);
accessor->fingerprint =
fmt("path:%s", store.queryPathInfo(*storePath)->narHash.to_string(HashFormat::SRI, true));
fmt("path:%s", store->queryPathInfo(*storePath)->narHash.to_string(HashFormat::SRI, true));
settings.getCache()->upsert(
makeFetchToStoreCacheKey(
input.getName(), *accessor->fingerprint, ContentAddressMethod::Raw::NixArchive, "/"),
store,
*store,
{},
*storePath);

View File

@@ -14,10 +14,10 @@ std::shared_ptr<Registry> Registry::read(const Settings & settings, const Source
{
debug("reading registry '%s'", path);
auto registry = std::make_shared<Registry>(type);
auto registry = std::make_shared<Registry>(settings, type);
if (!path.pathExists())
return std::make_shared<Registry>(type);
return std::make_shared<Registry>(settings, type);
try {
@@ -125,23 +125,23 @@ std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Pat
return customRegistry;
}
std::shared_ptr<Registry> getFlagRegistry()
std::shared_ptr<Registry> getFlagRegistry(const Settings & settings)
{
static auto flagRegistry = std::make_shared<Registry>(Registry::Flag);
static auto flagRegistry = std::make_shared<Registry>(settings, Registry::Flag);
return flagRegistry;
}
void overrideRegistry(const Input & from, const Input & to, const Attrs & extraAttrs)
void overrideRegistry(const Settings & settings, const Input & from, const Input & to, const Attrs & extraAttrs)
{
getFlagRegistry()->add(from, to, extraAttrs);
getFlagRegistry(settings)->add(from, to, extraAttrs);
}
static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, Store & store)
static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, ref<Store> store)
{
static auto reg = [&]() {
auto path = settings.flakeRegistry.get();
if (path == "") {
return std::make_shared<Registry>(Registry::Global); // empty registry
return std::make_shared<Registry>(settings, Registry::Global); // empty registry
}
return Registry::read(
@@ -149,9 +149,9 @@ static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, St
[&] -> SourcePath {
if (!isAbsolute(path)) {
auto storePath = downloadFile(store, settings, path, "flake-registry.json").storePath;
if (auto store2 = dynamic_cast<LocalFSStore *>(&store))
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json");
return {store.requireStoreObjectAccessor(storePath)};
return {store->requireStoreObjectAccessor(storePath)};
} else {
return SourcePath{getFSSourceAccessor(), CanonPath{path}}.resolveSymlinks();
}
@@ -162,10 +162,10 @@ static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, St
return reg;
}
Registries getRegistries(const Settings & settings, Store & store)
Registries getRegistries(const Settings & settings, ref<Store> store)
{
Registries registries;
registries.push_back(getFlagRegistry());
registries.push_back(getFlagRegistry(settings));
registries.push_back(getUserRegistry(settings));
registries.push_back(getSystemRegistry(settings));
registries.push_back(getGlobalRegistry(settings, store));
@@ -173,7 +173,7 @@ Registries getRegistries(const Settings & settings, Store & store)
}
std::pair<Input, Attrs>
lookupInRegistries(const Settings & settings, Store & store, const Input & _input, UseRegistries useRegistries)
lookupInRegistries(const Settings & settings, ref<Store> store, const Input & _input, UseRegistries useRegistries)
{
Attrs extraAttrs;
int n = 0;

View File

@@ -13,7 +13,7 @@
namespace nix::fetchers {
DownloadFileResult downloadFile(
Store & store,
ref<Store> store,
const Settings & settings,
const std::string & url,
const std::string & name,
@@ -28,7 +28,7 @@ DownloadFileResult downloadFile(
{"name", name},
}}};
auto cached = settings.getCache()->lookupStorePath(key, store);
auto cached = settings.getCache()->lookupStorePath(key, *store);
auto useCached = [&]() -> DownloadFileResult {
return {
@@ -74,7 +74,7 @@ DownloadFileResult downloadFile(
dumpString(res.data, sink);
auto hash = hashString(HashAlgorithm::SHA256, res.data);
auto info = ValidPathInfo::makeFromCA(
store,
*store,
name,
FixedOutputInfo{
.method = FileIngestionMethod::Flat,
@@ -84,7 +84,7 @@ DownloadFileResult downloadFile(
hashString(HashAlgorithm::SHA256, sink.s));
info.narSize = sink.s.size();
auto source = StringSource{sink.s};
store.addToStore(info, source, NoRepair, NoCheckSigs);
store->addToStore(info, source, NoRepair, NoCheckSigs);
storePath = std::move(info.path);
}
@@ -93,7 +93,7 @@ DownloadFileResult downloadFile(
key.second.insert_or_assign("url", url);
assert(!res.urls.empty());
infoAttrs.insert_or_assign("url", *res.urls.rbegin());
settings.getCache()->upsert(key, store, infoAttrs, *storePath);
settings.getCache()->upsert(key, *store, infoAttrs, *storePath);
}
return {
@@ -136,7 +136,7 @@ static DownloadTarballResult downloadTarball_(
.treeHash = treeHash,
.lastModified = (time_t) getIntAttr(infoAttrs, "lastModified"),
.immutableUrl = maybeGetStrAttr(infoAttrs, "immutableUrl"),
.accessor = settings.getTarballCache()->getAccessor(treeHash, {}, displayPrefix),
.accessor = settings.getTarballCache()->getAccessor(treeHash, false, displayPrefix),
};
};
@@ -214,7 +214,7 @@ static DownloadTarballResult downloadTarball_(
return attrsToResult(infoAttrs);
}
ref<SourceAccessor> downloadTarball(Store & store, const Settings & settings, const std::string & url)
ref<SourceAccessor> downloadTarball(ref<Store> store, const Settings & settings, const std::string & url)
{
/* Go through Input::getAccessor() to ensure that the resulting
accessor has a fingerprint. */
@@ -278,7 +278,7 @@ struct CurlInputScheme : InputScheme
HTTP request. Now that we've processed the Nix-specific
attributes above, remove them so we don't also send them as
part of the HTTP request. */
for (auto & [param, _] : allowedAttrs())
for (auto & param : allowedAttrs())
url.query.erase(param);
input.attrs.insert_or_assign("type", std::string{schemeName()});
@@ -286,83 +286,18 @@ struct CurlInputScheme : InputScheme
return input;
}
static const std::map<std::string, AttributeInfo> & allowedAttrsImpl()
StringSet allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = {
{
"url",
{
.type = "String",
.required = true,
.doc = R"(
Supported protocols:
- `https`
> **Example**
>
> ```nix
> fetchTree {
> type = "file";
> url = "https://example.com/index.html";
> }
> ```
- `http`
Insecure HTTP transfer for legacy sources.
> **Warning**
>
> HTTP performs no encryption or authentication.
> Use a `narHash` known in advance to ensure the output has expected contents.
- `file`
A file on the local file system.
> **Example**
>
> ```nix
> fetchTree {
> type = "file";
> url = "file:///home/eelco/nix/README.md";
> }
> ```
)",
},
},
{
"narHash",
{},
},
{
"name",
{},
},
{
"unpack",
{},
},
{
"rev",
{},
},
{
"revCount",
{},
},
{
"lastModified",
{},
},
return {
"type",
"url",
"narHash",
"name",
"unpack",
"rev",
"revCount",
"lastModified",
};
return attrs;
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
return allowedAttrsImpl();
}
std::optional<Input> inputFromAttrs(const Settings & settings, const Attrs & attrs) const override
@@ -397,14 +332,6 @@ struct FileInputScheme : CurlInputScheme
return "file";
}
std::string schemeDescription() const override
{
return stripIndentation(R"(
Place a plain file into the Nix store.
This is similar to [`builtins.fetchurl`](@docroot@/language/builtins.md#builtins-fetchurl)
)");
}
bool isValidURL(const ParsedURL & url, bool requireTree) const override
{
auto parsedUrlScheme = parseUrlScheme(url.scheme);
@@ -414,7 +341,7 @@ struct FileInputScheme : CurlInputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
{
auto input(_input);
@@ -424,10 +351,10 @@ struct FileInputScheme : CurlInputScheme
tarballs. */
auto file = downloadFile(store, settings, getStrAttr(input.attrs, "url"), input.getName());
auto narHash = store.queryPathInfo(file.storePath)->narHash;
auto narHash = store->queryPathInfo(file.storePath)->narHash;
input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
auto accessor = ref{store.getFSAccessor(file.storePath)};
auto accessor = ref{store->getFSAccessor(file.storePath)};
accessor->setPathDisplay("«" + input.to_string() + "»");
@@ -442,34 +369,6 @@ struct TarballInputScheme : CurlInputScheme
return "tarball";
}
std::string schemeDescription() const override
{
return stripIndentation(R"(
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)
)");
}
const std::map<std::string, AttributeInfo> & allowedAttrs() const override
{
static const std::map<std::string, AttributeInfo> attrs = [] {
auto attrs = CurlInputScheme::allowedAttrsImpl();
// Override the "url" attribute to add tarball-specific example
attrs["url"].doc = R"(
> **Example**
>
> ```nix
> fetchTree {
> type = "tarball";
> url = "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-23.11";
> }
> ```
)";
return attrs;
}();
return attrs;
}
bool isValidURL(const ParsedURL & url, bool requireTree) const override
{
auto parsedUrlScheme = parseUrlScheme(url.scheme);
@@ -480,7 +379,7 @@ struct TarballInputScheme : CurlInputScheme
}
std::pair<ref<SourceAccessor>, Input>
getAccessor(const Settings & settings, Store & store, const Input & _input) const override
getAccessor(const Settings & settings, ref<Store> store, const Input & _input) const override
{
auto input(_input);
@@ -505,7 +404,7 @@ struct TarballInputScheme : CurlInputScheme
return {result.accessor, input};
}
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 narHash = input.getNarHash())
return narHash->to_string(HashFormat::SRI, true);

View File

@@ -200,7 +200,7 @@ nix_value * nix_locked_flake_get_output_attrs(
nix_clear_err(context);
try {
auto v = nix_alloc_value(context, evalState);
nix::flake::callFlake(evalState->state, *lockedFlake->lockedFlake, *v->value);
nix::flake::callFlake(evalState->state, *lockedFlake->lockedFlake, v->value);
return v;
}
NIXC_CATCH_ERRS_NULL

View File

@@ -19,6 +19,7 @@
#include "nix/expr/eval-settings.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/value.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/fetchers/attrs.hh"
#include "nix/fetchers/fetchers.hh"
#include "nix/util/configuration.hh"
@@ -62,7 +63,7 @@ PrimOp getFlake(const Settings & settings)
return PrimOp{
.name = "__getFlake",
.args = {"args"},
.doc = R"(
.doc = &R"(
Fetch a flake from a flake reference, and return its output attributes and some metadata. For example:
```nix
@@ -76,7 +77,7 @@ PrimOp getFlake(const Settings & settings)
```nix
(builtins.getFlake "github:edolstra/dwarffs").rev
```
)",
)"_sds,
.fun = prim_getFlake,
.experimentalFeature = Xp::Flakes,
};
@@ -93,7 +94,7 @@ static void prim_parseFlakeRef(EvalState & state, const PosIdx pos, Value ** arg
auto & vv = binds.alloc(s);
std::visit(
overloaded{
[&vv, &state](const std::string & value) { vv.mkString(value, state.mem); },
[&vv](const std::string & value) { vv.mkString(value); },
[&vv](const uint64_t & value) { vv.mkInt(value); },
[&vv](const Explicit<bool> & value) { vv.mkBool(value.t); }},
value);
@@ -104,7 +105,7 @@ static void prim_parseFlakeRef(EvalState & state, const PosIdx pos, Value ** arg
nix::PrimOp parseFlakeRef({
.name = "__parseFlakeRef",
.args = {"flake-ref"},
.doc = R"(
.doc = &R"(
Parse a flake reference, and return its exploded form.
For example:
@@ -118,7 +119,7 @@ nix::PrimOp parseFlakeRef({
```nix
{ dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; }
```
)",
)"_sds,
.fun = prim_parseFlakeRef,
.experimentalFeature = Xp::Flakes,
});
@@ -156,13 +157,13 @@ static void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value **
}
}
auto flakeRef = FlakeRef::fromAttrs(state.fetchSettings, attrs);
v.mkString(flakeRef.to_string(), state.mem);
v.mkString(flakeRef.to_string());
}
nix::PrimOp flakeRefToString({
.name = "__flakeRefToString",
.args = {"attrs"},
.doc = R"(
.doc = &R"(
Convert a flake reference from attribute set format to URL format.
For example:
@@ -178,7 +179,7 @@ nix::PrimOp flakeRefToString({
```nix
"github:NixOS/nixpkgs/23.05?dir=lib"
```
)",
)"_sds,
.fun = prim_flakeRefToString,
.experimentalFeature = Xp::Flakes,
});

View File

@@ -373,7 +373,7 @@ static Flake getFlake(
{
// Fetch a lazy tree first.
auto cachedInput =
state.inputCache->getAccessor(state.fetchSettings, *state.store, originalRef.input, useRegistries);
state.inputCache->getAccessor(state.fetchSettings, state.store, originalRef.input, useRegistries);
auto subdir = fetchers::maybeGetStrAttr(cachedInput.extraAttrs, "dir").value_or(originalRef.subdir);
auto resolvedRef = FlakeRef(std::move(cachedInput.resolvedInput), subdir);
@@ -390,7 +390,7 @@ static Flake getFlake(
// FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'.
newLockedRef.input.attrs.erase("narHash");
auto cachedInput2 = state.inputCache->getAccessor(
state.fetchSettings, *state.store, newLockedRef.input, fetchers::UseRegistries::No);
state.fetchSettings, state.store, newLockedRef.input, fetchers::UseRegistries::No);
cachedInput.accessor = cachedInput2.accessor;
lockedRef = FlakeRef(std::move(cachedInput2.lockedInput), newLockedRef.subdir);
}
@@ -756,7 +756,7 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef,
return {*resolvedPath, *input.ref};
} else {
auto cachedInput = state.inputCache->getAccessor(
state.fetchSettings, *state.store, input.ref->input, useRegistriesInputs);
state.fetchSettings, state.store, input.ref->input, useRegistriesInputs);
auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), input.ref->subdir);
@@ -956,7 +956,7 @@ void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes)
auto key = keyMap.find(node);
assert(key != keyMap.end());
override.alloc(state.symbols.create("dir")).mkString(CanonPath(subdir).rel(), state.mem);
override.alloc(state.symbols.create("dir")).mkString(CanonPath(subdir).rel());
overrides.alloc(state.symbols.create(key->second)).mkAttrs(override);
}
@@ -966,7 +966,7 @@ void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes)
Value * vCallFlake = requireInternalFile(state, CanonPath("call-flake.nix"));
auto vLocks = state.allocValue();
vLocks->mkString(lockFileStr, state.mem);
vLocks->mkString(lockFileStr);
auto vFetchFinalTree = get(state.internalPrimOps, "fetchFinalTree");
assert(vFetchFinalTree);
@@ -975,7 +975,7 @@ void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes)
state.callFunction(*vCallFlake, args, vRes, noPos);
}
std::optional<Fingerprint> LockedFlake::getFingerprint(Store & store, const fetchers::Settings & fetchSettings) const
std::optional<Fingerprint> LockedFlake::getFingerprint(ref<Store> store, const fetchers::Settings & fetchSettings) const
{
if (lockFile.isUnlocked(fetchSettings))
return std::nullopt;
@@ -1005,7 +1005,7 @@ Flake::~Flake() {}
ref<eval_cache::EvalCache> openEvalCache(EvalState & state, ref<const LockedFlake> lockedFlake)
{
auto fingerprint = state.settings.useEvalCache && state.settings.pureEval
? lockedFlake->getFingerprint(*state.store, state.fetchSettings)
? lockedFlake->getFingerprint(state.store, state.fetchSettings)
: std::nullopt;
auto rootLoader = [&state, lockedFlake]() {
/* For testing whether the evaluation cache is

View File

@@ -64,8 +64,8 @@ std::ostream & operator<<(std::ostream & str, const FlakeRef & flakeRef)
return str;
}
FlakeRef
FlakeRef::resolve(const fetchers::Settings & fetchSettings, Store & store, fetchers::UseRegistries useRegistries) const
FlakeRef FlakeRef::resolve(
const fetchers::Settings & fetchSettings, ref<Store> store, fetchers::UseRegistries useRegistries) const
{
auto [input2, extraAttrs] = lookupInRegistries(fetchSettings, store, input, useRegistries);
return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir));
@@ -289,7 +289,7 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Settings & fetchSettings, const fet
}
std::pair<ref<SourceAccessor>, FlakeRef>
FlakeRef::lazyFetch(const fetchers::Settings & fetchSettings, Store & store) const
FlakeRef::lazyFetch(const fetchers::Settings & fetchSettings, ref<Store> store) const
{
auto [accessor, lockedInput] = input.getAccessor(fetchSettings, store);
return {accessor, FlakeRef(std::move(lockedInput), subdir)};

View File

@@ -135,7 +135,7 @@ struct LockedFlake
*/
std::map<ref<Node>, SourcePath> nodePaths;
std::optional<Fingerprint> getFingerprint(Store & store, const fetchers::Settings & fetchSettings) const;
std::optional<Fingerprint> getFingerprint(ref<Store> store, const fetchers::Settings & fetchSettings) const;
};
struct LockFlags

View File

@@ -73,12 +73,13 @@ struct FlakeRef
FlakeRef resolve(
const fetchers::Settings & fetchSettings,
Store & store,
ref<Store> store,
fetchers::UseRegistries useRegistries = fetchers::UseRegistries::All) const;
static FlakeRef fromAttrs(const fetchers::Settings & fetchSettings, const fetchers::Attrs & attrs);
std::pair<ref<SourceAccessor>, FlakeRef> lazyFetch(const fetchers::Settings & fetchSettings, Store & store) const;
std::pair<ref<SourceAccessor>, FlakeRef>
lazyFetch(const fetchers::Settings & fetchSettings, ref<Store> store) const;
/**
* Canonicalize a flakeref for the purpose of comparing "old" and

View File

@@ -35,8 +35,6 @@ include_dirs = [ include_directories('.') ]
headers = files(
'nix_api_store.h',
'nix_api_store/derivation.h',
'nix_api_store/store_path.h',
)
# TODO don't install this once tests don't use it and/or move the header into `libstore`, non-`c`

View File

@@ -1,6 +1,3 @@
#include <cstring>
#include <span>
#include "nix_api_store.h"
#include "nix_api_store_internal.h"
#include "nix_api_util.h"
@@ -11,7 +8,6 @@
#include "nix/store/store-open.hh"
#include "nix/store/build-result.hh"
#include "nix/store/local-fs-store.hh"
#include "nix/util/base-nix-32.hh"
#include "nix/store/globals.hh"
@@ -219,65 +215,7 @@ void nix_derivation_free(nix_derivation * drv)
StorePath * nix_store_path_clone(const StorePath * p)
{
try {
return new StorePath{p->path};
} catch (...) {
return nullptr;
}
}
}
template<size_t S>
static auto to_cpp_array(const uint8_t (&r)[S])
{
return reinterpret_cast<const std::array<std::byte, S> &>(r);
}
extern "C" {
nix_err
nix_store_path_hash(nix_c_context * context, const StorePath * store_path, nix_store_path_hash_part * hash_part_out)
{
try {
auto hashPart = store_path->path.hashPart();
// Decode from Nix32 (base32) encoding to raw bytes
auto decoded = nix::BaseNix32::decode(hashPart);
assert(decoded.size() == sizeof(hash_part_out->bytes));
std::memcpy(hash_part_out->bytes, decoded.data(), sizeof(hash_part_out->bytes));
return NIX_OK;
}
NIXC_CATCH_ERRS
}
StorePath * nix_store_create_from_parts(
nix_c_context * context, const nix_store_path_hash_part * hash, const char * name, size_t name_len)
{
if (context)
context->last_err_code = NIX_OK;
try {
// Encode the 20 raw bytes to Nix32 (base32) format
auto hashStr = nix::BaseNix32::encode(std::span<const std::byte>{to_cpp_array(hash->bytes)});
// Construct the store path basename: <hash>-<name>
std::string baseName;
baseName += hashStr;
baseName += "-";
baseName += std::string_view{name, name_len};
return new StorePath{nix::StorePath(std::move(baseName))};
}
NIXC_CATCH_ERRS_NULL
}
nix_derivation * nix_derivation_clone(const nix_derivation * d)
{
try {
return new nix_derivation{d->drv};
} catch (...) {
return nullptr;
}
return new StorePath{p->path};
}
nix_derivation * nix_derivation_from_json(nix_c_context * context, Store * store, const char * json)
@@ -285,25 +223,17 @@ nix_derivation * nix_derivation_from_json(nix_c_context * context, Store * store
if (context)
context->last_err_code = NIX_OK;
try {
return new nix_derivation{nix::Derivation::parseJsonAndValidate(*store->ptr, nlohmann::json::parse(json))};
auto drv = static_cast<nix::Derivation>(nlohmann::json::parse(json));
auto drvPath = nix::writeDerivation(*store->ptr, drv, nix::NoRepair, /* read only */ true);
drv.checkInvariants(*store->ptr, drvPath);
return new nix_derivation{drv};
}
NIXC_CATCH_ERRS_NULL
}
nix_err nix_derivation_to_json(
nix_c_context * context, const nix_derivation * drv, nix_get_string_callback callback, void * userdata)
{
if (context)
context->last_err_code = NIX_OK;
try {
auto result = static_cast<nlohmann::json>(drv->drv).dump();
if (callback) {
callback(result.data(), result.size(), userdata);
}
}
NIXC_CATCH_ERRS
}
StorePath * nix_add_derivation(nix_c_context * context, Store * store, nix_derivation * derivation)
{
if (context)
@@ -328,64 +258,4 @@ nix_err nix_store_copy_closure(nix_c_context * context, Store * srcStore, Store
NIXC_CATCH_ERRS
}
nix_derivation * nix_store_drv_from_store_path(nix_c_context * context, Store * store, const StorePath * path)
{
if (context)
context->last_err_code = NIX_OK;
try {
return new nix_derivation{store->ptr->derivationFromPath(path->path)};
}
NIXC_CATCH_ERRS_NULL
}
nix_err nix_store_query_path_info(
nix_c_context * context,
Store * store,
const StorePath * store_path,
void * userdata,
nix_get_string_callback callback)
{
if (context)
context->last_err_code = NIX_OK;
try {
auto info = store->ptr->queryPathInfo(store_path->path);
if (callback) {
auto result = info->toJSON(&*store->ptr, true).dump();
callback(result.data(), result.size(), userdata);
}
}
NIXC_CATCH_ERRS
}
nix_err nix_store_build_paths(
nix_c_context * context,
Store * store,
const StorePath ** store_paths,
unsigned int num_store_paths,
void (*callback)(void * userdata, const char * path, const char * result),
void * userdata)
{
if (context)
context->last_err_code = NIX_OK;
try {
std::span<const StorePath * const> paths_span(store_paths, num_store_paths);
std::vector<nix::DerivedPath> derived_paths;
for (const StorePath * store_path : paths_span) {
derived_paths.push_back(nix::SingleDerivedPath::Opaque{store_path->path});
}
auto results = store->ptr->buildPathsWithResults(derived_paths);
for (auto & result : results) {
if (callback) {
callback(
userdata,
result.path.to_string(store->ptr->config).c_str(),
static_cast<nlohmann::json>(result).dump().c_str());
}
}
}
NIXC_CATCH_ERRS
}
} // extern "C"

View File

@@ -12,8 +12,6 @@
*/
#include "nix_api_util.h"
#include "nix_api_store/store_path.h"
#include "nix_api_store/derivation.h"
#include <stdbool.h>
#ifdef __cplusplus
@@ -23,6 +21,10 @@ extern "C" {
/** @brief Reference to a Nix store */
typedef struct Store Store;
/** @brief Nix store path */
typedef struct StorePath StorePath;
/** @brief Nix Derivation */
typedef struct nix_derivation nix_derivation;
/**
* @brief Initializes the Nix store library
@@ -106,7 +108,7 @@ nix_err
nix_store_get_storedir(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data);
/**
* @brief Parse a Nix store path that includes the store dir into a StorePath
* @brief Parse a Nix store path into a StorePath
*
* @note Don't forget to free this path using nix_store_path_free()!
* @param[out] context Optional, stores error information
@@ -116,6 +118,30 @@ nix_store_get_storedir(nix_c_context * context, Store * store, nix_get_string_ca
*/
StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path);
/**
* @brief Get the path name (e.g. "name" in /nix/store/...-name)
*
* @param[in] store_path the path to get the name from
* @param[in] callback called with the name
* @param[in] user_data arbitrary data, passed to the callback when it's called.
*/
void nix_store_path_name(const StorePath * store_path, nix_get_string_callback callback, void * user_data);
/**
* @brief Copy a StorePath
*
* @param[in] p the path to copy
* @return a new StorePath
*/
StorePath * nix_store_path_clone(const StorePath * p);
/** @brief Deallocate a StorePath
*
* Does not fail.
* @param[in] p the path to free
*/
void nix_store_path_free(StorePath * p);
/**
* @brief Check if a StorePath is valid (i.e. that corresponding store object and its closure of references exists in
* the store)
@@ -188,16 +214,9 @@ nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_cal
/**
* @brief Create a `nix_derivation` from a JSON representation of that derivation.
*
* @note Unlike `nix_derivation_to_json`, this needs a `Store`. This is because
* over time we expect the internal representation of derivations in Nix to
* differ from accepted derivation formats. The store argument is here to help
* any logic needed to convert from JSON to the internal representation, in
* excess of just parsing.
*
* @param[out] context Optional, stores error information.
* @param[in] store nix store reference.
* @param[in] json JSON of the derivation as a string.
* @return A new derivation, or NULL on error. Free with `nix_derivation_free` when done using the `nix_derivation`.
*/
nix_derivation * nix_derivation_from_json(nix_c_context * context, Store * store, const char * json);
@@ -210,6 +229,14 @@ nix_derivation * nix_derivation_from_json(nix_c_context * context, Store * store
*/
StorePath * nix_add_derivation(nix_c_context * context, Store * store, nix_derivation * derivation);
/**
* @brief Deallocate a `nix_derivation`
*
* Does not fail.
* @param[in] drv the derivation to free
*/
void nix_derivation_free(nix_derivation * drv);
/**
* @brief Copy the closure of `path` from `srcStore` to `dstStore`.
*
@@ -249,56 +276,6 @@ nix_err nix_store_get_fs_closure(
void * userdata,
void (*callback)(nix_c_context * context, void * userdata, const StorePath * store_path));
/**
* @brief Returns the derivation associated with the store path
*
* @param[out] context Optional, stores error information
* @param[in] store The nix store
* @param[in] path The nix store path
* @return A new derivation, or NULL on error. Free with `nix_derivation_free` when done using the `nix_derivation`.
*/
nix_derivation * nix_store_drv_from_store_path(nix_c_context * context, Store * store, const StorePath * path);
/**
* @brief Queries for the nix store path info JSON.
*
* @param[out] context Optional, stores error information
* @param[in] store nix store reference
* @param[in] path A store path
* @param[in] userdata The data to pass to the callback
* @param[in] callback Called for when the path info is resolved
*/
nix_err nix_store_query_path_info(
nix_c_context * context,
Store * store,
const StorePath * store_path,
void * userdata,
nix_get_string_callback callback);
/**
* @brief Builds the paths, if they are a derivation then they get built.
*
* @note Path and result for the callback only exist for the lifetime of
* the call. Result is a string containing the build result in JSON.
*
* @param[out] context Optional, stores error information
* @param[in] store nix store reference
* @param[in] store_paths Pointer to list of nix store paths
* @param[in] num_store_paths Number of nix store paths
* @param[in] callback The callback to trigger for each build result. Called with:
* - userdata: user-provided data
* - path: the path that was built (string, only valid for duration of callback)
* - result: JSON string of the build result (only valid for duration of callback)
* @param[in] userdata User data to pass to the callback
*/
nix_err nix_store_build_paths(
nix_c_context * context,
Store * store,
const StorePath ** store_paths,
unsigned int num_store_paths,
void (*callback)(void * userdata, const char * path, const char * result),
void * userdata);
// cffi end
#ifdef __cplusplus
}

View File

@@ -1,57 +0,0 @@
#ifndef NIX_API_STORE_DERIVATION_H
#define NIX_API_STORE_DERIVATION_H
/**
* @defgroup libstore_derivation Derivation
* @ingroup libstore
* @brief Derivation operations that don't require a Store
* @{
*/
/** @file
* @brief Derivation operations
*/
#include "nix_api_util.h"
#ifdef __cplusplus
extern "C" {
#endif
// cffi start
/** @brief Nix Derivation */
typedef struct nix_derivation nix_derivation;
/**
* @brief Copy a `nix_derivation`
*
* @param[in] d the derivation to copy
* @return a new `nix_derivation`
*/
nix_derivation * nix_derivation_clone(const nix_derivation * d);
/**
* @brief Deallocate a `nix_derivation`
*
* Does not fail.
* @param[in] drv the derivation to free
*/
void nix_derivation_free(nix_derivation * drv);
/**
* @brief Gets the derivation as a JSON string
*
* @param[out] context Optional, stores error information
* @param[in] drv The derivation
* @param[in] callback Called with the JSON string
* @param[in] userdata Arbitrary data passed to the callback
*/
nix_err nix_derivation_to_json(
nix_c_context * context, const nix_derivation * drv, nix_get_string_callback callback, void * userdata);
// cffi end
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif // NIX_API_STORE_DERIVATION_H

View File

@@ -1,96 +0,0 @@
#ifndef NIX_API_STORE_STORE_PATH_H
#define NIX_API_STORE_STORE_PATH_H
/**
* @defgroup libstore_storepath StorePath
* @ingroup libstore
* @brief Store path operations that don't require a Store
* @{
*/
/** @file
* @brief Store path operations
*/
#include <stddef.h>
#include <stdint.h>
#include "nix_api_util.h"
#ifdef __cplusplus
extern "C" {
#endif
// cffi start
/** @brief Nix store path */
typedef struct StorePath StorePath;
/**
* @brief Copy a StorePath
*
* @param[in] p the path to copy
* @return a new StorePath
*/
StorePath * nix_store_path_clone(const StorePath * p);
/** @brief Deallocate a StorePath
*
* Does not fail.
* @param[in] p the path to free
*/
void nix_store_path_free(StorePath * p);
/**
* @brief Get the path name (e.g. "<name>" in /nix/store/<hash>-<name>)
*
* @param[in] store_path the path to get the name from
* @param[in] callback called with the name
* @param[in] user_data arbitrary data, passed to the callback when it's called.
*/
void nix_store_path_name(const StorePath * store_path, nix_get_string_callback callback, void * user_data);
/**
* @brief A store path hash
*
* Once decoded from "nix32" encoding, a store path hash is 20 raw bytes.
*/
typedef struct nix_store_path_hash_part
{
uint8_t bytes[20];
} nix_store_path_hash_part;
/**
* @brief Get the path hash (e.g. "<hash>" in /nix/store/<hash>-<name>)
*
* The hash is returned as raw bytes, decoded from "nix32" encoding.
*
* @param[out] context Optional, stores error information
* @param[in] store_path the path to get the hash from
* @param[out] hash_part_out the decoded hash as 20 raw bytes
* @return NIX_OK on success, error code on failure
*/
nix_err
nix_store_path_hash(nix_c_context * context, const StorePath * store_path, nix_store_path_hash_part * hash_part_out);
/**
* @brief Create a StorePath from its constituent parts (hash and name)
*
* This function constructs a store path from a hash and name, without needing
* a Store reference or the store directory prefix.
*
* @note Don't forget to free this path using nix_store_path_free()!
* @param[out] context Optional, stores error information
* @param[in] hash The store path hash (20 raw bytes)
* @param[in] name The store path name (the part after the hash)
* @param[in] name_len Length of the name string
* @return owned store path, NULL on error
*/
StorePath * nix_store_create_from_parts(
nix_c_context * context, const nix_store_path_hash_part * hash, const char name[/*name_len*/], size_t name_len);
// cffi end
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif // NIX_API_STORE_STORE_PATH_H

View File

@@ -50,8 +50,7 @@ protected:
#else
// resolve any symlinks in i.e. on macOS /tmp -> /private/tmp
// because this is not allowed for a nix store.
auto tmpl =
nix::absPath(std::filesystem::path(nix::defaultTempDir()) / "tests_nix-store.XXXXXX", std::nullopt, true);
auto tmpl = nix::absPath(std::filesystem::path(nix::defaultTempDir()) / "tests_nix-store.XXXXXX", true);
nixDir = mkdtemp((char *) tmpl.c_str());
#endif

View File

@@ -4,13 +4,10 @@
"allowSubstitutes": false,
"exportReferencesGraph": {
"refs1": [
{
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "out"
}
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"refs2": [
"qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
]
},
"impureEnvVars": [
@@ -23,36 +20,18 @@
"outputChecks": {
"forAllOutputs": {
"allowedReferences": [
{
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "out"
}
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"allowedRequisites": [
{
"drvPath": "self",
"output": "bin"
},
{
"drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",
"output": "dev"
}
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z",
"bin"
],
"disallowedReferences": [
{
"drvPath": "self",
"output": "dev"
},
{
"drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
"output": "out"
}
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g",
"dev"
],
"disallowedRequisites": [
{
"drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
"output": "dev"
}
"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"
],
"ignoreSelfRefs": true,
"maxClosureSize": null,

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