diff --git a/.editorconfig b/.editorconfig
index 887ecadba..86360e658 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -17,7 +17,7 @@ indent_style = space
indent_size = 2
# Match c++/shell/perl, set indent to spaces with width of four
-[*.{hpp,cc,hh,sh,pl}]
+[*.{hpp,cc,hh,sh,pl,xs}]
indent_style = space
indent_size = 4
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 12c60c649..975c90b91 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -21,7 +21,7 @@ jobs:
fetch-depth: 0
- name: Create backport PRs
# should be kept in sync with `version`
- uses: zeebe-io/backport-action@v1.4.0
+ uses: zeebe-io/backport-action@v2.1.1
with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/Makefile.config.in b/Makefile.config.in
index 19992fa20..1482db81f 100644
--- a/Makefile.config.in
+++ b/Makefile.config.in
@@ -28,6 +28,8 @@ SODIUM_LIBS = @SODIUM_LIBS@
SQLITE3_LIBS = @SQLITE3_LIBS@
bash = @bash@
bindir = @bindir@
+checkbindir = @checkbindir@
+checklibdir = @checklibdir@
datadir = @datadir@
datarootdir = @datarootdir@
doc_generate = @doc_generate@
@@ -48,4 +50,5 @@ sysconfdir = @sysconfdir@
system = @system@
ENABLE_BUILD = @ENABLE_BUILD@
ENABLE_TESTS = @ENABLE_TESTS@
+INSTALL_UNIT_TESTS = @INSTALL_UNIT_TESTS@
internal_api_docs = @internal_api_docs@
diff --git a/configure.ac b/configure.ac
index 225baf6b5..281ba2c32 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,6 +68,9 @@ case "$host_os" in
esac
+ENSURE_NO_GCC_BUG_80431
+
+
# Check for pubsetbuf.
AC_MSG_CHECKING([for pubsetbuf])
AC_LANG_PUSH(C++)
@@ -164,6 +167,18 @@ AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[Do not build the tests]),
ENABLE_TESTS=$enableval, ENABLE_TESTS=yes)
AC_SUBST(ENABLE_TESTS)
+AC_ARG_ENABLE(install-unit-tests, AS_HELP_STRING([--enable-install-unit-tests],[Install the unit tests for running later (default no)]),
+ INSTALL_UNIT_TESTS=$enableval, INSTALL_UNIT_TESTS=no)
+AC_SUBST(INSTALL_UNIT_TESTS)
+
+AC_ARG_WITH(check-bin-dir, AS_HELP_STRING([--with-check-bin-dir=PATH],[path to install unit tests for running later (defaults to $libexecdir/nix)]),
+ checkbindir=$withval, checkbindir=$libexecdir/nix)
+AC_SUBST(checkbindir)
+
+AC_ARG_WITH(check-lib-dir, AS_HELP_STRING([--with-check-lib-dir=PATH],[path to install unit tests for running later (defaults to $libdir)]),
+ checklibdir=$withval, checklibdir=$libdir)
+AC_SUBST(checklibdir)
+
# Building without API docs is the default as Nix' C++ interfaces are internal and unstable.
AC_ARG_ENABLE(internal_api_docs, AS_HELP_STRING([--enable-internal-api-docs],[Build API docs for Nix's internal unstable C++ interfaces]),
internal_api_docs=$enableval, internal_api_docs=no)
diff --git a/doc/manual/local.mk b/doc/manual/local.mk
index 8bf16e9dd..db3daf252 100644
--- a/doc/manual/local.mk
+++ b/doc/manual/local.mk
@@ -103,7 +103,7 @@ $(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/generate-settings.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(bindir)/nix
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
- $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-settings.nix { prefix = "opt-"; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
+ $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-settings.nix { prefix = "conf"; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
@mv $@.tmp $@
$(d)/nix.json: $(bindir)/nix
diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in
index 2fe77d2c6..8dc464abd 100644
--- a/doc/manual/src/SUMMARY.md.in
+++ b/doc/manual/src/SUMMARY.md.in
@@ -18,6 +18,8 @@
- [Uninstalling Nix](installation/uninstall.md)
- [Nix Store](store/index.md)
- [File System Object](store/file-system-object.md)
+ - [Store Object](store/store-object.md)
+ - [Store Path](store/store-path.md)
- [Nix Language](language/index.md)
- [Data Types](language/values.md)
- [Language Constructs](language/constructs.md)
@@ -113,6 +115,7 @@
- [C++ style guide](contributing/cxx.md)
- [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
+ - [Release 2.19 (2023-11-17)](release-notes/rl-2.19.md)
- [Release 2.18 (2023-09-20)](release-notes/rl-2.18.md)
- [Release 2.17 (2023-07-24)](release-notes/rl-2.17.md)
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)
diff --git a/doc/manual/src/architecture/architecture.md b/doc/manual/src/architecture/architecture.md
index 6e832e1f9..79429508f 100644
--- a/doc/manual/src/architecture/architecture.md
+++ b/doc/manual/src/architecture/architecture.md
@@ -63,7 +63,7 @@ The command line interface and Nix expressions are what users deal with most.
> The Nix language itself does not have a notion of *packages* or *configurations*.
> As far as we are concerned here, the inputs and results of a build plan are just data.
-Underlying the command line interface and the Nix language evaluator is the [Nix store](../glossary.md#gloss-store), a mechanism to keep track of build plans, data, and references between them.
+Underlying the command line interface and the Nix language evaluator is the [Nix store](../store/index.md), a mechanism to keep track of build plans, data, and references between them.
It can also execute build plans to produce new data, which are made available to the operating system as files.
A build plan itself is a series of *build tasks*, together with their build inputs.
diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md
index 190d367db..75226cd1a 100644
--- a/doc/manual/src/contributing/documentation.md
+++ b/doc/manual/src/contributing/documentation.md
@@ -162,6 +162,24 @@ Please observe these guidelines to ease reviews:
> This is a note.
```
+ Highlight examples as such:
+
+ ````
+ > **Example**
+ >
+ > ```console
+ > $ nix --version
+ > ```
+ ````
+
+ Highlight syntax definiions as such, using [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) notation:
+
+ ````
+ > **Syntax**
+ >
+ > *attribute-set* = `{` [ *attribute-name* `=` *expression* `;` ... ] `}`
+ ````
+
### The `@docroot@` variable
`@docroot@` provides a base path for links that occur in reusable snippets or other documentation that doesn't have a base path of its own.
diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md
index 38c144fcc..fe08ceb94 100644
--- a/doc/manual/src/contributing/hacking.md
+++ b/doc/manual/src/contributing/hacking.md
@@ -210,7 +210,7 @@ See [supported compilation environments](#compilation-environments) and instruct
To use the LSP with your editor, you first need to [set up `clangd`](https://clangd.llvm.org/installation#project-setup) by running:
```console
-make clean && bear -- make -j$NIX_BUILD_CORES install
+make clean && bear -- make -j$NIX_BUILD_CORES default check install
```
Configure your editor to use the `clangd` from the shell, either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration).
diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md
index 3d75ebe7b..0b45b88a3 100644
--- a/doc/manual/src/contributing/testing.md
+++ b/doc/manual/src/contributing/testing.md
@@ -133,17 +133,17 @@ ran test tests/functional/${testName}.sh... [PASS]
or without `make`:
```shell-session
-$ ./mk/run-test.sh tests/functional/${testName}.sh
+$ ./mk/run-test.sh tests/functional/${testName}.sh tests/functional/init.sh
ran test tests/functional/${testName}.sh... [PASS]
```
To see the complete output, one can also run:
```shell-session
-$ ./mk/debug-test.sh tests/functional/${testName}.sh
-+ foo
+$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh
++(${testName}.sh:1) foo
output from foo
-+ bar
++(${testName}.sh:2) bar
output from bar
...
```
@@ -175,7 +175,7 @@ edit it like so:
Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point:
```shell-session
-$ ./mk/debug-test.sh tests/functional/${testName}.sh
+$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh
...
+ gdb blash blub
GNU gdb (GDB) 12.1
diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md
index ad3cc147b..07891175a 100644
--- a/doc/manual/src/glossary.md
+++ b/doc/manual/src/glossary.md
@@ -59,7 +59,7 @@
- [store]{#gloss-store}
A collection of store objects, with operations to manipulate that collection.
- See [Nix Store] for details.
+ See [Nix store](./store/index.md) for details.
There are many types of stores.
See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md) for a complete list.
@@ -86,10 +86,13 @@
- [store path]{#gloss-store-path}
- The location of a [store object] in the file system, i.e., an
- immediate child of the Nix store directory.
+ The location of a [store object](@docroot@/store/index.md#store-object) in the file system, i.e., an immediate child of the Nix store directory.
- Example: `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1`
+ > **Example**
+ >
+ > `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1`
+
+ See [Store Path](@docroot@/store/store-path.md) for details.
[store path]: #gloss-store-path
diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md
index a3590f55d..a82ec5960 100644
--- a/doc/manual/src/language/constructs.md
+++ b/doc/manual/src/language/constructs.md
@@ -132,6 +132,32 @@ a = src-set.a; b = src-set.b; c = src-set.c;
when used while defining local variables in a let-expression or while
defining a set.
+In a `let` expression, `inherit` can be used to selectively bring specific attributes of a set into scope. For example
+
+
+```nix
+let
+ x = { a = 1; b = 2; };
+ inherit (builtins) attrNames;
+in
+{
+ names = attrNames x;
+}
+```
+
+is equivalent to
+
+```nix
+let
+ x = { a = 1; b = 2; };
+in
+{
+ names = builtins.attrNames x;
+}
+```
+
+both evaluate to `{ names = [ "a" "b" ]; }`.
+
## Functions
Functions have the following form:
@@ -146,65 +172,65 @@ three kinds of patterns:
- If a pattern is a single identifier, then the function matches any
argument. Example:
-
+
```nix
let negate = x: !x;
concat = x: y: x + y;
in if negate true then concat "foo" "bar" else ""
```
-
+
Note that `concat` is a function that takes one argument and returns
a function that takes another argument. This allows partial
parameterisation (i.e., only filling some of the arguments of a
function); e.g.,
-
+
```nix
map (concat "foo") [ "bar" "bla" "abc" ]
```
-
+
evaluates to `[ "foobar" "foobla" "fooabc" ]`.
- A *set pattern* of the form `{ name1, name2, …, nameN }` matches a
set containing the listed attributes, and binds the values of those
attributes to variables in the function body. For example, the
function
-
+
```nix
{ x, y, z }: z + y + x
```
-
+
can only be called with a set containing exactly the attributes `x`,
`y` and `z`. No other attributes are allowed. If you want to allow
additional arguments, you can use an ellipsis (`...`):
-
+
```nix
{ x, y, z, ... }: z + y + x
```
-
+
This works on any set that contains at least the three named
attributes.
-
+
It is possible to provide *default values* for attributes, in
which case they are allowed to be missing. A default value is
specified by writing `name ? e`, where *e* is an arbitrary
expression. For example,
-
+
```nix
{ x, y ? "foo", z ? "bar" }: z + y + x
```
-
+
specifies a function that only requires an attribute named `x`, but
optionally accepts `y` and `z`.
- An `@`-pattern provides a means of referring to the whole value
being matched:
-
+
```nix
args@{ x, y, z, ... }: z + y + x + args.a
```
-
+
but can also be written as:
-
+
```nix
{ x, y, z, ... } @ args: z + y + x + args.a
```
diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md
index cc825b4cf..e9cbb5c92 100644
--- a/doc/manual/src/language/operators.md
+++ b/doc/manual/src/language/operators.md
@@ -25,7 +25,7 @@
| Inequality | *expr* `!=` *expr* | none | 11 |
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
| Logical disjunction (`OR`) | *bool* \|\| *bool* | left | 13 |
-| [Logical implication] | *bool* `->` *bool* | none | 14 |
+| [Logical implication] | *bool* `->` *bool* | right | 14 |
[string]: ./values.md#type-string
[path]: ./values.md#type-path
diff --git a/doc/manual/src/release-notes/rl-2.19.md b/doc/manual/src/release-notes/rl-2.19.md
new file mode 100644
index 000000000..4eecaf929
--- /dev/null
+++ b/doc/manual/src/release-notes/rl-2.19.md
@@ -0,0 +1,77 @@
+# Release 2.19 (2023-11-17)
+
+- The experimental `nix` command can now act as a [shebang interpreter](@docroot@/command-ref/new-cli/nix.md#shebang-interpreter)
+ by appending the contents of any `#! nix` lines and the script's location into a single call.
+
+- [URL flake references](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references) now support [percent-encoded](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1) characters.
+
+- [Path-like flake references](@docroot@/command-ref/new-cli/nix3-flake.md#path-like-syntax) now accept arbitrary unicode characters (except `#` and `?`).
+
+- The experimental feature `repl-flake` is no longer needed, as its functionality is now part of the `flakes` experimental feature. To get the previous behavior, use the `--file/--expr` flags accordingly.
+
+- There is a new flake installable syntax `flakeref#.attrPath` where the "." prefix specifies that `attrPath` is interpreted from the root of the flake outputs, with no searching of default attribute prefixes like `packages.` or `legacyPackages.`.
+
+- Nix adds `apple-virt` to the default system features on macOS systems that support virtualization. This is similar to what's done for the `kvm` system feature on Linux hosts.
+
+- Add a new built-in function [`builtins.convertHash`](@docroot@/language/builtins.md#builtins-convertHash).
+
+- `nix-shell` shebang lines now support single-quoted arguments.
+
+- `builtins.fetchTree` is now its own experimental feature, [`fetch-tree`](@docroot@/contributing/experimental-features.md#xp-fetch-tree).
+ As described in the documentation for that feature, this is because we anticipate polishing it and then stabilizing it before the rest of flakes.
+
+- The interface for creating and updating lock files has been overhauled:
+
+ - [`nix flake lock`](@docroot@/command-ref/new-cli/nix3-flake-lock.md) only creates lock files and adds missing inputs now.
+ It will *never* update existing inputs.
+
+ - [`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md) does the same, but *will* update inputs.
+ - Passing no arguments will update all inputs of the current flake, just like it already did.
+ - Passing input names as arguments will ensure only those are updated. This replaces the functionality of `nix flake lock --update-input`
+ - To operate on a flake outside the current directory, you must now pass `--flake path/to/flake`.
+
+ - The flake-specific flags `--recreate-lock-file` and `--update-input` have been removed from all commands operating on installables.
+ They are superceded by `nix flake update`.
+
+- Commit signature verification for the [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit) is added as the new [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches).
+
+- [`nix path-info --json`](@docroot@/command-ref/new-cli/nix3-path-info.md)
+ (experimental) now returns a JSON map rather than JSON list.
+ The `path` field of each object has instead become the key in the outer map, since it is unique.
+ The `valid` field also goes away because we just use `null` instead.
+
+ - Old way:
+
+ ```json5
+ [
+ {
+ "path": "/nix/store/8fv91097mbh5049i9rglc73dx6kjg3qk-bash-5.2-p15",
+ "valid": true,
+ // ...
+ },
+ {
+ "path": "/nix/store/wffw7l0alvs3iw94cbgi1gmmbmw99sqb-home-manager-path",
+ "valid": false
+ }
+ ]
+ ```
+
+ - New way
+
+ ```json5
+ {
+ "/nix/store/8fv91097mbh5049i9rglc73dx6kjg3qk-bash-5.2-p15": {
+ // ...
+ },
+ "/nix/store/wffw7l0alvs3iw94cbgi1gmmbmw99sqb-home-manager-path": null,
+ }
+ ```
+
+ This makes it match `nix derivation show`, which also maps store paths to information.
+
+- When Nix is installed using the [binary installer](@docroot@/installation/installing-binary.md), in supported shells (Bash, Zsh, Fish)
+ [`XDG_DATA_DIRS`](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables) is now populated with the path to the `/share` subdirectory of the current profile.
+ This means that command completion scripts, `.desktop` files, and similar artifacts installed via [`nix-env`](@docroot@/command-ref/nix-env.md) or [`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md)
+ (experimental) can be found by any program that follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
+
+- A new command `nix store add` has been added. It replaces `nix store add-file` and `nix store add-path` which are now deprecated.
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index 2163a5392..dbe2692f9 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1,19 +1,3 @@
# Release X.Y (202?-??-??)
-- [URL flake references](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references) now support [percent-encoded](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1) characters.
-
-- [Path-like flake references](@docroot@/command-ref/new-cli/nix3-flake.md#path-like-syntax) now accept arbitrary unicode characters (except `#` and `?`).
-
-- The experimental feature `repl-flake` is no longer needed, as its functionality is now part of the `flakes` experimental feature. To get the previous behavior, use the `--file/--expr` flags accordingly.
-
-- Introduce new flake installable syntax `flakeref#.attrPath` where the "." prefix denotes no searching of default attribute prefixes like `packages.` or `legacyPackages.`.
-
-- Nix adds `apple-virt` to the default system features on macOS systems that support virtualization. This is similar to what's done for the `kvm` system feature on Linux hosts.
-
-- Introduce a new built-in function [`builtins.convertHash`](@docroot@/language/builtins.md#builtins-convertHash).
-
-- Fixed a bug where `nix-env --query` ignored `--drv-path` when `--json` was set.
-
-- `nix-shell` shebang lines now support single-quoted arguments.
-
-- `builtins.fetchTree` is now marked as stable.
+- Fixed a bug where `nix-env --query` ignored `--drv-path` when `--json` was set.
\ No newline at end of file
diff --git a/doc/manual/src/store/index.md b/doc/manual/src/store/index.md
index 316e04179..8a5305062 100644
--- a/doc/manual/src/store/index.md
+++ b/doc/manual/src/store/index.md
@@ -1,4 +1,5 @@
# Nix Store
-The *Nix store* is an abstraction used by Nix to store immutable filesystem artifacts (such as software packages) that can have dependencies (*references*) between them.
-There are multiple implementations of the Nix store, such as the actual filesystem (`/nix/store`) and binary caches.
+The *Nix store* is an abstraction to store immutable file system data (such as software packages) that can have dependencies on other such data.
+
+There are multiple implementations of Nix stores with different capabilities, such as the actual filesystem (`/nix/store`) or binary caches.
diff --git a/doc/manual/src/store/store-object.md b/doc/manual/src/store/store-object.md
new file mode 100644
index 000000000..caf5657d1
--- /dev/null
+++ b/doc/manual/src/store/store-object.md
@@ -0,0 +1,10 @@
+## Store Object
+
+A Nix store is a collection of *store objects* with *references* between them.
+A store object consists of
+
+ - A [file system object](./file-system-object.md) as data
+ - A set of [store paths](./store-path.md) as references to other store objects
+
+Store objects are [immutable](https://en.wikipedia.org/wiki/Immutable_object):
+Once created, they do not change until they are deleted.
diff --git a/doc/manual/src/store/store-path.md b/doc/manual/src/store/store-path.md
new file mode 100644
index 000000000..b5ad0c654
--- /dev/null
+++ b/doc/manual/src/store/store-path.md
@@ -0,0 +1,69 @@
+# Store Path
+
+Nix implements references to [store objects](./index.md#store-object) as *store paths*.
+
+Think of a store path as an [opaque], [unique identifier]:
+The only way to obtain store path is by adding or building store objects.
+A store path will always reference exactly one store object.
+
+[opaque]: https://en.m.wikipedia.org/wiki/Opaque_data_type
+[unique identifier]: https://en.m.wikipedia.org/wiki/Unique_identifier
+
+Store paths are pairs of
+
+- A 20-byte digest for identification
+- A symbolic name for people to read
+
+> **Example**
+>
+> - Digest: `b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z`
+> - Name: `firefox-33.1`
+
+To make store objects accessible to operating system processes, stores have to expose store objects through the file system.
+
+A store path is rendered to a file system path as the concatenation of
+
+- [Store directory](#store-directory) (typically `/nix/store`)
+- Path separator (`/`)
+- Digest rendered in a custom variant of [Base32](https://en.wikipedia.org/wiki/Base32) (20 arbitrary bytes become 32 ASCII characters)
+- Hyphen (`-`)
+- Name
+
+> **Example**
+>
+> ```
+> /nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1
+> |--------| |------------------------------| |----------|
+> store directory digest name
+> ```
+
+## Store Directory
+
+Every [Nix store](./index.md) has a store directory.
+
+Not every store can be accessed through the file system.
+But if the store has a file system representation, the store directory contains the store’s [file system objects], which can be addressed by [store paths](#store-path).
+
+[file system objects]: ./file-system-object.md
+
+This means a store path is not just derived from the referenced store object itself, but depends on the store the store object is in.
+
+> **Note**
+>
+> The store directory defaults to `/nix/store`, but is in principle arbitrary.
+
+It is important which store a given store object belongs to:
+Files in the store object can contain store paths, and processes may read these paths.
+Nix can only guarantee referential integrity if store paths do not cross store boundaries.
+
+Therefore one can only copy store objects to a different store if
+
+- The source and target stores' directories match
+
+ or
+
+- The store object in question has no references, that is, contains no store paths
+
+One cannot copy a store object to a store with a different store directory.
+Instead, it has to be rebuilt, together with all its dependencies.
+It is in general not enough to replace the store directory string in file contents, as this may render executables unusable by invalidating their internal offsets or checksums.
diff --git a/flake.lock b/flake.lock
index 56df9c3fb..991cef1ee 100644
--- a/flake.lock
+++ b/flake.lock
@@ -34,16 +34,16 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1695283060,
- "narHash": "sha256-CJz71xhCLlRkdFUSQEL0pIAAfcnWFXMzd9vXhPrnrEg=",
+ "lastModified": 1698876495,
+ "narHash": "sha256-nsQo2/mkDUFeAjuu92p0dEqhRvHHiENhkKVIV1y0/Oo=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "31ed632c692e6a36cfc18083b88ece892f863ed4",
+ "rev": "9eb24edd6a0027fed010ccfe300a9734d029983c",
"type": "github"
},
"original": {
"owner": "NixOS",
- "ref": "nixos-23.05-small",
+ "ref": "release-23.05",
"repo": "nixpkgs",
"type": "github"
}
diff --git a/flake.nix b/flake.nix
index 398ba10a0..05ab7b06d 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,7 +1,9 @@
{
description = "The purely functional package manager";
- inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small";
+ # FIXME go back to nixos-23.05-small once
+ # https://github.com/NixOS/nixpkgs/pull/264875 is included.
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-23.05";
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
@@ -162,6 +164,10 @@
testConfigureFlags = [
"RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
+ ] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
+ "--enable-install-unit-tests"
+ "--with-check-bin-dir=${builtins.placeholder "check"}/bin"
+ "--with-check-lib-dir=${builtins.placeholder "check"}/lib"
];
internalApiDocsConfigureFlags = [
@@ -183,6 +189,7 @@
buildPackages.git
buildPackages.mercurial # FIXME: remove? only needed for tests
buildPackages.jq # Also for custom mdBook preprocessor.
+ buildPackages.openssh # only needed for tests (ssh-keygen)
]
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
@@ -401,7 +408,8 @@
src = nixSrc;
VERSION_SUFFIX = versionSuffix;
- outputs = [ "out" "dev" "doc" ];
+ outputs = [ "out" "dev" "doc" ]
+ ++ lib.optional (currentStdenv.hostPlatform != currentStdenv.buildPlatform) "check";
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps
@@ -707,7 +715,8 @@
stdenv.mkDerivation {
name = "nix";
- outputs = [ "out" "dev" "doc" ];
+ outputs = [ "out" "dev" "doc" ]
+ ++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) "check";
nativeBuildInputs = nativeBuildDeps
++ lib.optional stdenv.cc.isClang pkgs.buildPackages.bear
diff --git a/m4/gcc_bug_80431.m4 b/m4/gcc_bug_80431.m4
new file mode 100644
index 000000000..e42f01956
--- /dev/null
+++ b/m4/gcc_bug_80431.m4
@@ -0,0 +1,64 @@
+# Ensure that this bug is not present in the C++ toolchain we are using.
+#
+# URL for bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431
+#
+# The test program is from that issue, with only a slight modification
+# to set an exit status instead of printing strings.
+AC_DEFUN([ENSURE_NO_GCC_BUG_80431],
+[
+ AC_MSG_CHECKING([that GCC bug 80431 is fixed])
+ AC_LANG_PUSH(C++)
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+ #include
+
+ static bool a = true;
+ static bool b = true;
+
+ struct Options { };
+
+ struct Option
+ {
+ Option(Options * options)
+ {
+ a = false;
+ }
+
+ ~Option()
+ {
+ b = false;
+ }
+ };
+
+ struct MyOptions : Options { };
+
+ struct MyOptions2 : virtual MyOptions
+ {
+ Option foo{this};
+ };
+ ]],
+ [[
+ {
+ MyOptions2 opts;
+ }
+ return (a << 1) | b;
+ ]])],
+ [status_80431=0],
+ [status_80431=$?],
+ [
+ # Assume we're bug-free when cross-compiling
+ ])
+ AC_LANG_POP(C++)
+ AS_CASE([$status_80431],
+ [0],[
+ AC_MSG_RESULT(yes)
+ ],
+ [2],[
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR(Cannot build Nix with C++ compiler with this bug)
+ ],
+ [
+ AC_MSG_RESULT(unexpected result $status_80431: not expected failure with bug, ignoring)
+ ])
+])
diff --git a/maintainers/README.md b/maintainers/README.md
index 5be4f9d04..ee97c1195 100644
--- a/maintainers/README.md
+++ b/maintainers/README.md
@@ -2,7 +2,7 @@
## Motivation
-The team's main responsibility is to set a direction for the development of Nix and ensure that the code is in good shape.
+The team's main responsibility is to guide and direct the development of Nix and ensure that the code is in good shape.
We aim to achieve this by improving the contributor experience and attracting more maintainers – that is, by helping other people contributing to Nix and eventually taking responsibility – in order to scale the development process to match users' needs.
diff --git a/mk/common-test.sh b/mk/common-test.sh
index 7ab25febf..00ccd1584 100644
--- a/mk/common-test.sh
+++ b/mk/common-test.sh
@@ -1,15 +1,27 @@
-test_dir=tests/functional
+# Remove overall test dir (at most one of the two should match) and
+# remove file extension.
+test_name=$(echo -n "$test" | sed \
+ -e "s|^unit-test-data/||" \
+ -e "s|^tests/functional/||" \
+ -e "s|\.sh$||" \
+ )
-test=$(echo -n "$test" | sed -e "s|^$test_dir/||")
-
-TESTS_ENVIRONMENT=("TEST_NAME=${test%.*}" 'NIX_REMOTE=')
+TESTS_ENVIRONMENT=(
+ "TEST_NAME=$test_name"
+ 'NIX_REMOTE='
+ 'PS4=+(${BASH_SOURCE[0]-$0}:$LINENO) '
+)
: ${BASH:=/usr/bin/env bash}
+run () {
+ cd "$(dirname $1)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -x -e -u -o pipefail $(basename $1)
+}
+
init_test () {
- cd "$test_dir" && env "${TESTS_ENVIRONMENT[@]}" $BASH -e init.sh 2>/dev/null > /dev/null
+ run "$init" 2>/dev/null > /dev/null
}
run_test_proper () {
- cd "$test_dir/$(dirname $test)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -e $(basename $test)
+ run "$test"
}
diff --git a/mk/debug-test.sh b/mk/debug-test.sh
index b5b628ecd..52482c01e 100755
--- a/mk/debug-test.sh
+++ b/mk/debug-test.sh
@@ -3,9 +3,12 @@
set -eu -o pipefail
test=$1
+init=${2-}
dir="$(dirname "${BASH_SOURCE[0]}")"
source "$dir/common-test.sh"
-(init_test)
+if [ -n "$init" ]; then
+ (init_test)
+fi
run_test_proper
diff --git a/mk/lib.mk b/mk/lib.mk
index e86a7f1a4..49abe9862 100644
--- a/mk/lib.mk
+++ b/mk/lib.mk
@@ -122,14 +122,15 @@ $(foreach script, $(bin-scripts), $(eval $(call install-program-in,$(script),$(b
$(foreach script, $(bin-scripts), $(eval programs-list += $(script)))
$(foreach script, $(noinst-scripts), $(eval programs-list += $(script)))
$(foreach template, $(template-files), $(eval $(call instantiate-template,$(template))))
+install_test_init=tests/functional/init.sh
$(foreach test, $(install-tests), \
- $(eval $(call run-install-test,$(test))) \
+ $(eval $(call run-test,$(test),$(install_test_init))) \
$(eval installcheck: $(test).test))
$(foreach test-group, $(install-tests-groups), \
- $(eval $(call run-install-test-group,$(test-group))) \
+ $(eval $(call run-test-group,$(test-group),$(install_test_init))) \
$(eval installcheck: $(test-group).test-group) \
$(foreach test, $($(test-group)-tests), \
- $(eval $(call run-install-test,$(test))) \
+ $(eval $(call run-test,$(test),$(install_test_init))) \
$(eval $(test-group).test-group: $(test).test)))
$(foreach file, $(man-pages), $(eval $(call install-data-in, $(file), $(mandir)/man$(patsubst .%,%,$(suffix $(file))))))
diff --git a/mk/run-test.sh b/mk/run-test.sh
index 1a1d65930..da9c5a473 100755
--- a/mk/run-test.sh
+++ b/mk/run-test.sh
@@ -8,6 +8,7 @@ yellow=""
normal=""
test=$1
+init=${2-}
dir="$(dirname "${BASH_SOURCE[0]}")"
source "$dir/common-test.sh"
@@ -21,7 +22,9 @@ if [ -t 1 ]; then
fi
run_test () {
- (init_test 2>/dev/null > /dev/null)
+ if [ -n "$init" ]; then
+ (init_test 2>/dev/null > /dev/null)
+ fi
log="$(run_test_proper 2>&1)" && status=0 || status=$?
}
diff --git a/mk/tests.mk b/mk/tests.mk
index ec8128bdf..bac9b704a 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -2,19 +2,22 @@
test-deps =
-define run-install-test
+define run-bash
- .PHONY: $1.test
- $1.test: $1 $(test-deps)
- @env BASH=$(bash) $(bash) mk/run-test.sh $1 < /dev/null
-
- .PHONY: $1.test-debug
- $1.test-debug: $1 $(test-deps)
- @env BASH=$(bash) $(bash) mk/debug-test.sh $1 < /dev/null
+ .PHONY: $1
+ $1: $2
+ @env BASH=$(bash) $(bash) $3 < /dev/null
endef
-define run-install-test-group
+define run-test
+
+ $(eval $(call run-bash,$1.test,$1 $(test-deps),mk/run-test.sh $1 $2))
+ $(eval $(call run-bash,$1.test-debug,$1 $(test-deps),mk/debug-test.sh $1 $2))
+
+endef
+
+define run-test-group
.PHONY: $1.test-group
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index 08f812b31..f89ac4077 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -11,7 +11,6 @@
#include "derivations.hh"
#include "globals.hh"
#include "store-api.hh"
-#include "util.hh"
#include "crypto.hh"
#include
diff --git a/scripts/nix-profile-daemon.fish.in b/scripts/nix-profile-daemon.fish.in
index 400696812..c23aa64f0 100644
--- a/scripts/nix-profile-daemon.fish.in
+++ b/scripts/nix-profile-daemon.fish.in
@@ -19,6 +19,14 @@ set __ETC_PROFILE_NIX_SOURCED 1
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
+# Populate bash completions, .desktop files, etc
+if test -z "$XDG_DATA_DIRS"
+ # According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
+ set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:/nix/var/nix/profiles/default/share"
+else
+ set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:/nix/var/nix/profiles/default/share"
+end
+
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if test -n "$NIX_SSH_CERT_FILE"
: # Allow users to override the NIX_SSL_CERT_FILE
diff --git a/scripts/nix-profile-daemon.sh.in b/scripts/nix-profile-daemon.sh.in
index 8cfd3149e..c63db4648 100644
--- a/scripts/nix-profile-daemon.sh.in
+++ b/scripts/nix-profile-daemon.sh.in
@@ -30,6 +30,14 @@ fi
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
+# Populate bash completions, .desktop files, etc
+if [ -z "$XDG_DATA_DIRS" ]; then
+ # According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
+ export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
+else
+ export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
+fi
+
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
: # Allow users to override the NIX_SSL_CERT_FILE
diff --git a/scripts/nix-profile.fish.in b/scripts/nix-profile.fish.in
index 731498c76..619df52b8 100644
--- a/scripts/nix-profile.fish.in
+++ b/scripts/nix-profile.fish.in
@@ -20,6 +20,14 @@ if test -n "$HOME" && test -n "$USER"
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
+ # Populate bash completions, .desktop files, etc
+ if test -z "$XDG_DATA_DIRS"
+ # According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
+ set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
+ else
+ set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
+ end
+
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if test -n "$NIX_SSH_CERT_FILE"
: # Allow users to override the NIX_SSL_CERT_FILE
diff --git a/scripts/nix-profile.sh.in b/scripts/nix-profile.sh.in
index c4d60cf37..56e070ae1 100644
--- a/scripts/nix-profile.sh.in
+++ b/scripts/nix-profile.sh.in
@@ -32,6 +32,14 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
+ # Populate bash completions, .desktop files, etc
+ if [ -z "$XDG_DATA_DIRS" ]; then
+ # According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
+ export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
+ else
+ export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
+ fi
+
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
diff --git a/src/libcmd/built-path.hh b/src/libcmd/built-path.hh
index e677bc810..7154cc504 100644
--- a/src/libcmd/built-path.hh
+++ b/src/libcmd/built-path.hh
@@ -1,3 +1,6 @@
+#pragma once
+///@file
+
#include "derived-path.hh"
#include "realisation.hh"
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index a88ba8134..de9f546fc 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -175,7 +175,7 @@ void BuiltPathsCommand::run(ref store, Installables && installables)
throw UsageError("'--all' does not expect arguments");
// XXX: Only uses opaque paths, ignores all the realisations
for (auto & p : store->queryAllValidPaths())
- paths.push_back(BuiltPath::Opaque{p});
+ paths.emplace_back(BuiltPath::Opaque{p});
} else {
paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
if (recursive) {
@@ -188,7 +188,7 @@ void BuiltPathsCommand::run(ref store, Installables && installables)
}
store->computeFSClosure(pathsRoots, pathsClosure);
for (auto & path : pathsClosure)
- paths.push_back(BuiltPath::Opaque{path});
+ paths.emplace_back(BuiltPath::Opaque{path});
}
}
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index dafc0db3b..120c832ac 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -326,6 +326,12 @@ struct MixEnvironment : virtual Args {
void setEnviron();
};
+void completeFlakeInputPath(
+ AddCompletions & completions,
+ ref evalState,
+ const std::vector & flakeRefs,
+ std::string_view prefix);
+
void completeFlakeRef(AddCompletions & completions, ref store, std::string_view prefix);
void completeFlakeRefWithFragment(
diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc
index e53bc4c01..401acc38e 100644
--- a/src/libcmd/common-eval-args.cc
+++ b/src/libcmd/common-eval-args.cc
@@ -2,7 +2,6 @@
#include "common-eval-args.hh"
#include "shared.hh"
#include "filetransfer.hh"
-#include "util.hh"
#include "eval.hh"
#include "fetchers.hh"
#include "registry.hh"
@@ -165,7 +164,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish();
}
-SourcePath lookupFileArg(EvalState & state, std::string_view s)
+SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDir)
{
if (EvalSettings::isPseudoUrl(s)) {
auto storePath = fetchers::downloadTarball(
@@ -186,7 +185,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s)
}
else
- return state.rootPath(CanonPath::fromCwd(s));
+ return state.rootPath(CanonPath(s, baseDir));
}
}
diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh
index 6359b2579..4b403d936 100644
--- a/src/libcmd/common-eval-args.hh
+++ b/src/libcmd/common-eval-args.hh
@@ -2,6 +2,7 @@
///@file
#include "args.hh"
+#include "canon-path.hh"
#include "common-args.hh"
#include "search-path.hh"
@@ -28,6 +29,6 @@ private:
std::map autoArgs;
};
-SourcePath lookupFileArg(EvalState & state, std::string_view s);
+SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDir = CanonPath::fromCwd());
}
diff --git a/src/libcmd/editor-for.cc b/src/libcmd/editor-for.cc
index a17c6f12a..619d3673f 100644
--- a/src/libcmd/editor-for.cc
+++ b/src/libcmd/editor-for.cc
@@ -1,5 +1,5 @@
-#include "util.hh"
#include "editor-for.hh"
+#include "environment-variables.hh"
namespace nix {
diff --git a/src/libcmd/installable-attr-path.hh b/src/libcmd/installable-attr-path.hh
index e9f0c33da..86c2f8219 100644
--- a/src/libcmd/installable-attr-path.hh
+++ b/src/libcmd/installable-attr-path.hh
@@ -4,7 +4,6 @@
#include "globals.hh"
#include "installable-value.hh"
#include "outputs-spec.hh"
-#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index eff18bbf6..1c6103020 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -4,6 +4,7 @@
#include "installable-attr-path.hh"
#include "installable-flake.hh"
#include "outputs-spec.hh"
+#include "users.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
@@ -28,7 +29,7 @@
namespace nix {
-static void completeFlakeInputPath(
+void completeFlakeInputPath(
AddCompletions & completions,
ref evalState,
const std::vector & flakeRefs,
@@ -46,13 +47,6 @@ MixFlakeOptions::MixFlakeOptions()
{
auto category = "Common flake-related options";
- addFlag({
- .longName = "recreate-lock-file",
- .description = "Recreate the flake's lock file from scratch.",
- .category = category,
- .handler = {&lockFlags.recreateLockFile, true}
- });
-
addFlag({
.longName = "no-update-lock-file",
.description = "Do not allow any updates to the flake's lock file.",
@@ -85,19 +79,6 @@ MixFlakeOptions::MixFlakeOptions()
.handler = {&lockFlags.commitLockFile, true}
});
- addFlag({
- .longName = "update-input",
- .description = "Update a specific flake input (ignoring its previous entry in the lock file).",
- .category = category,
- .labels = {"input-path"},
- .handler = {[&](std::string s) {
- lockFlags.inputUpdates.insert(flake::parseInputPath(s));
- }},
- .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
- completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
- }}
- });
-
addFlag({
.longName = "override-input",
.description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`). This implies `--no-write-lock-file`.",
@@ -107,7 +88,7 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.writeLockFile = false;
lockFlags.inputOverrides.insert_or_assign(
flake::parseInputPath(inputPath),
- parseFlakeRef(flakeRef, absPath("."), true));
+ parseFlakeRef(flakeRef, absPath(getCommandBaseDir()), true));
}},
.completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) {
if (n == 0) {
@@ -149,7 +130,7 @@ MixFlakeOptions::MixFlakeOptions()
auto evalState = getEvalState();
auto flake = flake::lockFlake(
*evalState,
- parseFlakeRef(flakeRef, absPath(".")),
+ parseFlakeRef(flakeRef, absPath(getCommandBaseDir())),
{ .writeLockFile = false });
for (auto & [inputName, input] : flake.lockFile.root->inputs) {
auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes
@@ -313,6 +294,8 @@ void completeFlakeRefWithFragment(
prefixRoot = ".";
}
auto flakeRefS = std::string(prefix.substr(0, hash));
+
+ // TODO: ideally this would use the command base directory instead of assuming ".".
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
auto evalCache = openEvalCache(*evalState,
@@ -461,10 +444,12 @@ Installables SourceExprCommand::parseInstallables(
auto e = state->parseStdin();
state->eval(e, *vFile);
}
- else if (file)
- state->evalFile(lookupFileArg(*state, *file), *vFile);
+ else if (file) {
+ state->evalFile(lookupFileArg(*state, *file, CanonPath::fromCwd(getCommandBaseDir())), *vFile);
+ }
else {
- auto e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
+ CanonPath dir(CanonPath::fromCwd(getCommandBaseDir()));
+ auto e = state->parseExprFromString(*expr, state->rootPath(dir));
state->eval(e, *vFile);
}
@@ -499,7 +484,7 @@ Installables SourceExprCommand::parseInstallables(
}
try {
- auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath("."));
+ auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath(getCommandBaseDir()));
result.push_back(make_ref(
this,
getEvalState(),
@@ -683,7 +668,7 @@ BuiltPaths Installable::toBuiltPaths(
BuiltPaths res;
for (auto & drvPath : Installable::toDerivations(store, installables, true))
- res.push_back(BuiltPath::Opaque{drvPath});
+ res.emplace_back(BuiltPath::Opaque{drvPath});
return res;
}
}
@@ -773,7 +758,7 @@ std::vector RawInstallablesCommand::getFlakeRefsForCompletion()
for (auto i : rawInstallables)
res.push_back(parseFlakeRefWithFragment(
expandTilde(i),
- absPath(".")).first);
+ absPath(getCommandBaseDir())).first);
return res;
}
@@ -795,7 +780,7 @@ std::vector InstallableCommand::getFlakeRefsForCompletion()
return {
parseFlakeRefWithFragment(
expandTilde(_installable),
- absPath(".")).first
+ absPath(getCommandBaseDir())).first
};
}
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index b0dc0dc02..e087f935c 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -1,7 +1,6 @@
#pragma once
///@file
-#include "util.hh"
#include "path.hh"
#include "outputs-spec.hh"
#include "derived-path.hh"
diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc
index 668a07763..8b3bbc1b5 100644
--- a/src/libcmd/markdown.cc
+++ b/src/libcmd/markdown.cc
@@ -1,6 +1,7 @@
#include "markdown.hh"
#include "util.hh"
#include "finally.hh"
+#include "terminal.hh"
#include
#include
diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc
index 2e17a29a7..bf5643a5c 100644
--- a/src/libcmd/repl.cc
+++ b/src/libcmd/repl.cc
@@ -22,6 +22,7 @@ extern "C" {
#include "repl.hh"
#include "ansicolor.hh"
+#include "signals.hh"
#include "shared.hh"
#include "eval.hh"
#include "eval-cache.hh"
@@ -36,6 +37,8 @@ extern "C" {
#include "globals.hh"
#include "flake/flake.hh"
#include "flake/lockfile.hh"
+#include "users.hh"
+#include "terminal.hh"
#include "editor-for.hh"
#include "finally.hh"
#include "markdown.hh"
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index d12345710..7481a2232 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -1,6 +1,5 @@
#include "attr-path.hh"
#include "eval-inline.hh"
-#include "util.hh"
namespace nix {
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 10fc799a9..6c0e33709 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -1,3 +1,4 @@
+#include "users.hh"
#include "eval-cache.hh"
#include "sqlite.hh"
#include "eval.hh"
diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc
index 93b4a5289..444a7d7d6 100644
--- a/src/libexpr/eval-settings.cc
+++ b/src/libexpr/eval-settings.cc
@@ -1,3 +1,4 @@
+#include "users.hh"
#include "globals.hh"
#include "profiles.hh"
#include "eval.hh"
diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh
index 5473d688e..db2971acb 100644
--- a/src/libexpr/eval-settings.hh
+++ b/src/libexpr/eval-settings.hh
@@ -1,4 +1,6 @@
#pragma once
+///@file
+
#include "config.hh"
namespace nix {
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index d26cde423..e9b8cacfd 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1,6 +1,7 @@
#include "eval.hh"
#include "eval-settings.hh"
#include "hash.hh"
+#include "primops.hh"
#include "types.hh"
#include "util.hh"
#include "store-api.hh"
@@ -14,6 +15,7 @@
#include "print.hh"
#include "fs-input-accessor.hh"
#include "memory-input-accessor.hh"
+#include "signals.hh"
#include
#include
@@ -29,6 +31,7 @@
#include
#include
+#include
#if HAVE_BOEHMGC
@@ -721,6 +724,23 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
}
+void PrimOp::check()
+{
+ if (arity > maxPrimOpArity) {
+ throw Error("primop arity must not exceed %1%", maxPrimOpArity);
+ }
+}
+
+
+void Value::mkPrimOp(PrimOp * p)
+{
+ p->check();
+ clearValue();
+ internalType = tPrimOp;
+ primOp = p;
+}
+
+
Value * EvalState::addPrimOp(PrimOp && primOp)
{
/* Hack to make constants lazy: turn them into a application of
@@ -1690,7 +1710,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* We have all the arguments, so call the primop with
the previous and new arguments. */
- Value * vArgs[arity];
+ Value * vArgs[maxPrimOpArity];
auto n = argsDone;
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left)
vArgs[--n] = arg->primOpApp.right;
@@ -1747,11 +1767,17 @@ void ExprCall::eval(EvalState & state, Env & env, Value & v)
Value vFun;
fun->eval(state, env, vFun);
- Value * vArgs[args.size()];
+ // Empirical arity of Nixpkgs lambdas by regex e.g. ([a-zA-Z]+:(\s|(/\*.*\/)|(#.*\n))*){5}
+ // 2: over 4000
+ // 3: about 300
+ // 4: about 60
+ // 5: under 10
+ // This excluded attrset lambdas (`{...}:`). Contributions of mixed lambdas appears insignificant at ~150 total.
+ boost::container::small_vector vArgs(args.size());
for (size_t i = 0; i < args.size(); ++i)
vArgs[i] = args[i]->maybeThunk(state, env);
- state.callFunction(vFun, args.size(), vArgs, v, pos);
+ state.callFunction(vFun, args.size(), vArgs.data(), v, pos);
}
@@ -1990,8 +2016,8 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
return result;
};
- Value values[es->size()];
- Value * vTmpP = values;
+ boost::container::small_vector values(es->size());
+ Value * vTmpP = values.data();
for (auto & [i_pos, i] : *es) {
Value & vTmp = *vTmpP++;
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 048dff42b..9a92992c1 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -18,6 +18,12 @@
namespace nix {
+/**
+ * We put a limit on primop arity because it lets us use a fixed size array on
+ * the stack. 8 is already an impractical number of arguments. Use an attrset
+ * argument for such overly complicated functions.
+ */
+constexpr size_t maxPrimOpArity = 8;
class Store;
class EvalState;
@@ -71,6 +77,12 @@ struct PrimOp
* Optional experimental for this to be gated on.
*/
std::optional experimentalFeature;
+
+ /**
+ * Validity check to be performed by functions that introduce primops,
+ * such as RegisterPrimOp() and Value::mkPrimOp().
+ */
+ void check();
};
/**
@@ -827,7 +839,7 @@ std::string showType(const Value & v);
/**
* If `path` refers to a directory, then append "/default.nix".
*/
-SourcePath resolveExprPath(const SourcePath & path);
+SourcePath resolveExprPath(SourcePath path);
struct InvalidPathError : EvalError
{
diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc
index e89014862..3c7ed5d8a 100644
--- a/src/libexpr/flake/config.cc
+++ b/src/libexpr/flake/config.cc
@@ -1,6 +1,7 @@
-#include "flake.hh"
+#include "users.hh"
#include "globals.hh"
#include "fetch-settings.hh"
+#include "flake.hh"
#include
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index be2cf014c..54de53e0b 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -1,3 +1,4 @@
+#include "terminal.hh"
#include "flake.hh"
#include "eval.hh"
#include "eval-settings.hh"
@@ -8,6 +9,7 @@
#include "fetchers.hh"
#include "finally.hh"
#include "fetch-settings.hh"
+#include "value-to-json.hh"
namespace nix {
@@ -140,8 +142,13 @@ static FlakeInput parseFlakeInput(EvalState & state,
attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
break;
default:
- throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
- state.symbols[attr.name], showType(*attr.value));
+ if (attr.name == state.symbols.create("publicKeys")) {
+ experimentalFeatureSettings.require(Xp::VerifiedFetches);
+ NixStringContext emptyContext = {};
+ attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, emptyContext).dump());
+ } else
+ throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
+ state.symbols[attr.name], showType(*attr.value));
}
#pragma GCC diagnostic pop
}
@@ -447,8 +454,8 @@ LockedFlake lockFlake(
assert(input.ref);
- /* Do we have an entry in the existing lock file? And we
- don't have a --update-input flag for this input? */
+ /* Do we have an entry in the existing lock file?
+ And the input is not in updateInputs? */
std::shared_ptr oldLock;
updatesUsed.insert(inputPath);
@@ -472,9 +479,8 @@ LockedFlake lockFlake(
node->inputs.insert_or_assign(id, childNode);
- /* If we have an --update-input flag for an input
- of this input, then we must fetch the flake to
- update it. */
+ /* If we have this input in updateInputs, then we
+ must fetch the flake to update it. */
auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
auto mustRefetch =
@@ -616,19 +622,14 @@ LockedFlake lockFlake(
for (auto & i : lockFlags.inputUpdates)
if (!updatesUsed.count(i))
- warn("the flag '--update-input %s' does not match any input", printInputPath(i));
+ warn("'%s' does not match any input of this flake", printInputPath(i));
/* Check 'follows' inputs. */
newLockFile.check();
debug("new lock file: %s", newLockFile);
- auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
auto sourcePath = topRef.input.getSourcePath();
- auto outputLockFilePath = sourcePath ? std::optional{*sourcePath + "/" + relPath} : std::nullopt;
- if (lockFlags.outputLockFilePath) {
- outputLockFilePath = lockFlags.outputLockFilePath;
- }
/* Check whether we need to / can write the new lock file. */
if (newLockFile != oldLockFile || lockFlags.outputLockFilePath) {
@@ -636,7 +637,7 @@ LockedFlake lockFlake(
auto diff = LockFile::diff(oldLockFile, newLockFile);
if (lockFlags.writeLockFile) {
- if (outputLockFilePath) {
+ if (sourcePath || lockFlags.outputLockFilePath) {
if (auto unlockedInput = newLockFile.isUnlocked()) {
if (fetchSettings.warnDirty)
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
@@ -644,41 +645,48 @@ LockedFlake lockFlake(
if (!lockFlags.updateLockFile)
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
- bool lockFileExists = pathExists(*outputLockFilePath);
+ auto newLockFileS = fmt("%s\n", newLockFile);
+
+ if (lockFlags.outputLockFilePath) {
+ if (lockFlags.commitLockFile)
+ throw Error("'--commit-lock-file' and '--output-lock-file' are incompatible");
+ writeFile(*lockFlags.outputLockFilePath, newLockFileS);
+ } else {
+ auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
+ auto outputLockFilePath = *sourcePath + "/" + relPath;
+
+ bool lockFileExists = pathExists(outputLockFilePath);
- if (lockFileExists) {
auto s = chomp(diff);
- if (s.empty())
- warn("updating lock file '%s'", *outputLockFilePath);
- else
- warn("updating lock file '%s':\n%s", *outputLockFilePath, s);
- } else
- warn("creating lock file '%s'", *outputLockFilePath);
+ if (lockFileExists) {
+ if (s.empty())
+ warn("updating lock file '%s'", outputLockFilePath);
+ else
+ warn("updating lock file '%s':\n%s", outputLockFilePath, s);
+ } else
+ warn("creating lock file '%s': \n%s", outputLockFilePath, s);
- newLockFile.write(*outputLockFilePath);
+ std::optional commitMessage = std::nullopt;
- std::optional commitMessage = std::nullopt;
- if (lockFlags.commitLockFile) {
- if (lockFlags.outputLockFilePath) {
- throw Error("--commit-lock-file and --output-lock-file are currently incompatible");
- }
- std::string cm;
+ if (lockFlags.commitLockFile) {
+ std::string cm;
- cm = fetchSettings.commitLockFileSummary.get();
+ cm = fetchSettings.commitLockFileSummary.get();
- if (cm == "") {
- cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
+ if (cm == "") {
+ cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
+ }
+
+ cm += "\n\nFlake lock file updates:\n\n";
+ cm += filterANSIEscapes(diff, true);
+ commitMessage = cm;
}
- cm += "\n\nFlake lock file updates:\n\n";
- cm += filterANSIEscapes(diff, true);
- commitMessage = cm;
+ topRef.input.putFile(
+ CanonPath((topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock"),
+ newLockFileS, commitMessage);
}
- topRef.input.markChangedFile(
- (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
- commitMessage);
-
/* Rewriting the lockfile changed the top-level
repo, so we should re-read it. FIXME: we could
also just clear the 'rev' field... */
diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc
index f3ea9063f..3e99fb2d4 100644
--- a/src/libexpr/flake/lockfile.cc
+++ b/src/libexpr/flake/lockfile.cc
@@ -214,12 +214,6 @@ std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
return stream;
}
-void LockFile::write(const Path & path) const
-{
- createDirs(dirOf(path));
- writeFile(path, fmt("%s\n", *this));
-}
-
std::optional LockFile::isUnlocked() const
{
std::set> nodes;
diff --git a/src/libexpr/flake/lockfile.hh b/src/libexpr/flake/lockfile.hh
index ba4c0c848..5a1493404 100644
--- a/src/libexpr/flake/lockfile.hh
+++ b/src/libexpr/flake/lockfile.hh
@@ -65,8 +65,6 @@ struct LockFile
static LockFile read(const Path & path);
- void write(const Path & path) const;
-
/**
* Check whether this lock file has any unlocked inputs.
*/
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index fe3e6f7ee..d4e946d81 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -1,5 +1,4 @@
#include "get-drvs.hh"
-#include "util.hh"
#include "eval-inline.hh"
#include "derivations.hh"
#include "store-api.hh"
diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk
index d243b9cec..ed7bf9490 100644
--- a/src/libexpr/local.mk
+++ b/src/libexpr/local.mk
@@ -43,7 +43,9 @@ $(foreach i, $(wildcard src/libexpr/value/*.hh), \
$(foreach i, $(wildcard src/libexpr/flake/*.hh), \
$(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644)))
-$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh
+$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh
+
+$(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh
$(d)/flake/flake.cc: $(d)/flake/call-flake.nix.gen.hh
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 607795937..f6cf1f689 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -19,6 +19,7 @@
#include
#include "util.hh"
+#include "users.hh"
#include "nixexpr.hh"
#include "eval.hh"
@@ -685,17 +686,25 @@ Expr * EvalState::parse(
}
-SourcePath resolveExprPath(const SourcePath & path)
+SourcePath resolveExprPath(SourcePath path)
{
+ unsigned int followCount = 0, maxFollow = 1024;
+
/* If `path' is a symlink, follow it. This is so that relative
path references work. */
- auto path2 = path.resolveSymlinks();
+ while (true) {
+ // Basic cycle/depth limit to avoid infinite loops.
+ if (++followCount >= maxFollow)
+ throw Error("too many symbolic links encountered while traversing the path '%s'", path);
+ if (path.lstat().type != InputAccessor::tSymlink) break;
+ path = {path.accessor, CanonPath(path.readLink(), path.path.parent().value_or(CanonPath::root))};
+ }
/* If `path' refers to a directory, append `/default.nix'. */
- if (path2.lstat().type == InputAccessor::tDirectory)
- return path2 + "default.nix";
+ if (path.lstat().type == InputAccessor::tDirectory)
+ return path + "default.nix";
- return path2;
+ return path;
}
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 704e7007b..e274c3c0c 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -10,6 +10,7 @@
#include "path-references.hh"
#include "store-api.hh"
#include "util.hh"
+#include "processes.hh"
#include "value-to-json.hh"
#include "value-to-xml.hh"
#include "primops.hh"
@@ -28,7 +29,6 @@
#include
-
namespace nix {
@@ -824,7 +824,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
auto message = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.addErrorContext",
false, false).toOwned();
- e.addTrace(nullptr, message, true);
+ e.addTrace(nullptr, hintfmt(message), true);
throw;
}
}
@@ -1548,10 +1548,8 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args,
try {
auto checked = state.checkSourcePath(path);
- auto exists = checked.pathExists();
- if (exists && mustBeDir) {
- exists = checked.lstat().type == InputAccessor::tDirectory;
- }
+ auto st = checked.maybeLstat();
+ auto exists = st && (!mustBeDir || st->type == SourceAccessor::tDirectory);
v.mkBool(exists);
} catch (SysError & e) {
/* Don't give away info from errors while canonicalising
@@ -2551,6 +2549,7 @@ static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args
/* Get the attribute names to be removed.
We keep them as Attrs instead of Symbols so std::set_difference
can be used to remove them from attrs[0]. */
+ // 64: large enough to fit the attributes of a derivation
boost::container::small_vector names;
names.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) {
@@ -2730,8 +2729,8 @@ static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, V
auto attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.catAttrs"));
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.catAttrs");
- Value * res[args[1]->listSize()];
- unsigned int found = 0;
+ boost::container::small_vector res(args[1]->listSize());
+ size_t found = 0;
for (auto v2 : args[1]->listItems()) {
state.forceAttrs(*v2, pos, "while evaluating an element in the list passed as second argument to builtins.catAttrs");
@@ -3065,9 +3064,8 @@ static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Val
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filter");
- // FIXME: putting this on the stack is risky.
- Value * vs[args[1]->listSize()];
- unsigned int k = 0;
+ boost::container::small_vector vs(args[1]->listSize());
+ size_t k = 0;
bool same = true;
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
@@ -3192,10 +3190,14 @@ static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value * * ar
state.forceFunction(*args[0], pos, std::string("while evaluating the first argument passed to builtins.") + (any ? "any" : "all"));
state.forceList(*args[1], pos, std::string("while evaluating the second argument passed to builtins.") + (any ? "any" : "all"));
+ std::string_view errorCtx = any
+ ? "while evaluating the return value of the function passed to builtins.any"
+ : "while evaluating the return value of the function passed to builtins.all";
+
Value vTmp;
for (auto elem : args[1]->listItems()) {
state.callFunction(*args[0], *elem, vTmp, pos);
- bool res = state.forceBool(vTmp, pos, std::string("while evaluating the return value of the function passed to builtins.") + (any ? "any" : "all"));
+ bool res = state.forceBool(vTmp, pos, errorCtx);
if (res == any) {
v.mkBool(any);
return;
@@ -3451,7 +3453,7 @@ static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args,
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.concatMap");
auto nrLists = args[1]->listSize();
- Value lists[nrLists];
+ boost::container::small_vector lists(nrLists);
size_t len = 0;
for (unsigned int n = 0; n < nrLists; ++n) {
diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh
index 930e7f32a..45486608f 100644
--- a/src/libexpr/primops.hh
+++ b/src/libexpr/primops.hh
@@ -8,6 +8,22 @@
namespace nix {
+/**
+ * For functions where we do not expect deep recursion, we can use a sizable
+ * part of the stack a free allocation space.
+ *
+ * Note: this is expected to be multiplied by sizeof(Value), or about 24 bytes.
+ */
+constexpr size_t nonRecursiveStackReservation = 128;
+
+/**
+ * Functions that maybe applied to self-similar inputs, such as concatMap on a
+ * tree, should reserve a smaller part of the stack for allocation.
+ *
+ * Note: this is expected to be multiplied by sizeof(Value), or about 24 bytes.
+ */
+constexpr size_t conservativeStackReservation = 16;
+
struct RegisterPrimOp
{
typedef std::vector PrimOps;
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 767f559be..8031bf809 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -7,6 +7,7 @@
#include "registry.hh"
#include "tarball.hh"
#include "url.hh"
+#include "value-to-json.hh"
#include
#include
@@ -125,6 +126,10 @@ static void fetchTree(
attrs.emplace(state.symbols[attr.name], Explicit{attr.value->boolean});
else if (attr.value->type() == nInt)
attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer));
+ else if (state.symbols[attr.name] == "publicKeys") {
+ experimentalFeatureSettings.require(Xp::VerifiedFetches);
+ attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
+ }
else
state.debugThrowLastTrace(TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
state.symbols[attr.name], showType(*attr.value)));
@@ -223,6 +228,7 @@ static RegisterPrimOp primop_fetchTree({
```
)",
.fun = prim_fetchTree,
+ .experimentalFeature = Xp::FetchTree,
});
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
@@ -427,6 +433,42 @@ static RegisterPrimOp primop_fetchGit({
With this argument being true, it's possible to load a `rev` from *any* `ref`
(by default only `rev`s from the specified `ref` are supported).
+ - `verifyCommit` (default: `true` if `publicKey` or `publicKeys` are provided, otherwise `false`)
+
+ Whether to check `rev` for a signature matching `publicKey` or `publicKeys`.
+ If `verifyCommit` is enabled, then `fetchGit` cannot use a local repository with uncommitted changes.
+ Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches).
+
+ - `publicKey`
+
+ The public key against which `rev` is verified if `verifyCommit` is enabled.
+ Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches).
+
+ - `keytype` (default: `"ssh-ed25519"`)
+
+ The key type of `publicKey`.
+ Possible values:
+ - `"ssh-dsa"`
+ - `"ssh-ecdsa"`
+ - `"ssh-ecdsa-sk"`
+ - `"ssh-ed25519"`
+ - `"ssh-ed25519-sk"`
+ - `"ssh-rsa"`
+ Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches).
+
+ - `publicKeys`
+
+ The public keys against which `rev` is verified if `verifyCommit` is enabled.
+ Must be given as a list of attribute sets with the following form:
+ ```nix
+ {
+ key = "";
+ type = ""; # optional, default: "ssh-ed25519"
+ }
+ ```
+ Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches).
+
+
Here are some examples of how to use `fetchGit`.
- To fetch a private repository over SSH:
@@ -501,6 +543,21 @@ static RegisterPrimOp primop_fetchGit({
}
```
+ - To verify the commit signature:
+
+ ```nix
+ builtins.fetchGit {
+ url = "ssh://git@github.com/nixos/nix.git";
+ verifyCommit = true;
+ publicKeys = [
+ {
+ type = "ssh-ed25519";
+ key = "AAAAC3NzaC1lZDI1NTE5AAAAIArPKULJOid8eS6XETwUjO48/HKBWl7FTCK0Z//fplDi";
+ }
+ ];
+ }
+ ```
+
Nix will refetch the branch according to the [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl) setting.
This behavior is disabled in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
diff --git a/src/libexpr/search-path.cc b/src/libexpr/search-path.cc
index 180d5f8b1..a25767496 100644
--- a/src/libexpr/search-path.cc
+++ b/src/libexpr/search-path.cc
@@ -1,5 +1,4 @@
#include "search-path.hh"
-#include "util.hh"
namespace nix {
diff --git a/src/libexpr/tests/local.mk b/src/libexpr/tests/local.mk
index 331a5ead6..6d2a04aaf 100644
--- a/src/libexpr/tests/local.mk
+++ b/src/libexpr/tests/local.mk
@@ -6,7 +6,11 @@ libexpr-tests_NAME := libnixexpr-tests
libexpr-tests_DIR := $(d)
-libexpr-tests_INSTALL_DIR :=
+ifeq ($(INSTALL_UNIT_TESTS), yes)
+ libexpr-tests_INSTALL_DIR := $(checkbindir)
+else
+ libexpr-tests_INSTALL_DIR :=
+endif
libexpr-tests_SOURCES := \
$(wildcard $(d)/*.cc) \
diff --git a/src/libexpr/tests/value/print.cc b/src/libexpr/tests/value/print.cc
index 5e96e12ec..a4f6fc014 100644
--- a/src/libexpr/tests/value/print.cc
+++ b/src/libexpr/tests/value/print.cc
@@ -114,7 +114,8 @@ TEST_F(ValuePrintingTests, vLambda)
TEST_F(ValuePrintingTests, vPrimOp)
{
Value vPrimOp;
- vPrimOp.mkPrimOp(nullptr);
+ PrimOp primOp{};
+ vPrimOp.mkPrimOp(&primOp);
test(vPrimOp, "");
}
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index cbc91f509..74b3ebf13 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -1,7 +1,7 @@
#include "value-to-json.hh"
#include "eval-inline.hh"
-#include "util.hh"
#include "store-api.hh"
+#include "signals.hh"
#include
#include
diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc
index bd7a4ae30..5032115bb 100644
--- a/src/libexpr/value-to-xml.cc
+++ b/src/libexpr/value-to-xml.cc
@@ -1,7 +1,7 @@
#include "value-to-xml.hh"
#include "xml-writer.hh"
#include "eval-inline.hh"
-#include "util.hh"
+#include "signals.hh"
#include
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 622e613ea..191cc30ba 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -354,13 +354,7 @@ public:
// Value will be overridden anyways
}
- inline void mkPrimOp(PrimOp * p)
- {
- clearValue();
- internalType = tPrimOp;
- primOp = p;
- }
-
+ void mkPrimOp(PrimOp * p);
inline void mkPrimOpApp(Value * l, Value * r)
{
diff --git a/src/libexpr/value/context.cc b/src/libexpr/value/context.cc
index 22361d8fa..6d9633268 100644
--- a/src/libexpr/value/context.cc
+++ b/src/libexpr/value/context.cc
@@ -1,3 +1,4 @@
+#include "util.hh"
#include "value/context.hh"
#include
diff --git a/src/libexpr/value/context.hh b/src/libexpr/value/context.hh
index 9f1d59317..51fd30a44 100644
--- a/src/libexpr/value/context.hh
+++ b/src/libexpr/value/context.hh
@@ -1,7 +1,6 @@
#pragma once
///@file
-#include "util.hh"
#include "comparator.hh"
#include "derived-path.hh"
#include "variant-wrapper.hh"
diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc
index 0c8ecac9d..b72a464e8 100644
--- a/src/libfetchers/cache.cc
+++ b/src/libfetchers/cache.cc
@@ -1,4 +1,5 @@
#include "cache.hh"
+#include "users.hh"
#include "sqlite.hh"
#include "sync.hh"
#include "store-api.hh"
diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh
index ae398d040..af34e66ce 100644
--- a/src/libfetchers/cache.hh
+++ b/src/libfetchers/cache.hh
@@ -2,6 +2,7 @@
///@file
#include "fetchers.hh"
+#include "path.hh"
namespace nix::fetchers {
diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh
index 6108a179c..f095963a8 100644
--- a/src/libfetchers/fetch-settings.hh
+++ b/src/libfetchers/fetch-settings.hh
@@ -3,7 +3,6 @@
#include "types.hh"
#include "config.hh"
-#include "util.hh"
#include