Merge branch 'master' into angerman/mac-fix-recursive-nix

This commit is contained in:
Eelco Dolstra 2023-06-09 13:06:47 +02:00 committed by GitHub
commit 381a32981b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
229 changed files with 4173 additions and 1916 deletions

View file

@ -21,7 +21,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Create backport PRs - name: Create backport PRs
# should be kept in sync with `version` # should be kept in sync with `version`
uses: zeebe-io/backport-action@v1.2.0 uses: zeebe-io/backport-action@v1.3.0
with: with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action # Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -19,7 +19,10 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: cachix/install-nix-action@v20 - uses: cachix/install-nix-action@v21
with:
# The sandbox would otherwise be disabled by default on Darwin
extra_nix_config: "sandbox = true"
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v12 - uses: cachix/cachix-action@v12
if: needs.check_secrets.outputs.cachix == 'true' if: needs.check_secrets.outputs.cachix == 'true'
@ -58,7 +61,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v20 - uses: cachix/install-nix-action@v21
with: with:
install_url: https://releases.nixos.org/nix/nix-2.13.3/install install_url: https://releases.nixos.org/nix/nix-2.13.3/install
- uses: cachix/cachix-action@v12 - uses: cachix/cachix-action@v12
@ -79,7 +82,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v20 - uses: cachix/install-nix-action@v21
with: with:
install_url: '${{needs.installer.outputs.installerURL}}' install_url: '${{needs.installer.outputs.installerURL}}'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve" install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
@ -106,7 +109,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: cachix/install-nix-action@v20 - uses: cachix/install-nix-action@v21
with: with:
install_url: https://releases.nixos.org/nix/nix-2.13.3/install install_url: https://releases.nixos.org/nix/nix-2.13.3/install
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV

3
.gitignore vendored
View file

@ -51,6 +51,8 @@ perl/Makefile.config
/src/nix/nix /src/nix/nix
/src/nix/doc
# /src/nix-env/ # /src/nix-env/
/src/nix-env/nix-env /src/nix-env/nix-env
@ -85,6 +87,7 @@ perl/Makefile.config
/tests/shell.drv /tests/shell.drv
/tests/config.nix /tests/config.nix
/tests/ca/config.nix /tests/ca/config.nix
/tests/dyn-drv/config.nix
/tests/repl-result-out /tests/repl-result-out
# /tests/lang/ # /tests/lang/

View file

@ -1 +1 @@
2.16.0 2.17.0

View file

@ -30,6 +30,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy).
You can use [labels](https://github.com/NixOS/nix/labels) to filter for relevant topics. You can use [labels](https://github.com/NixOS/nix/labels) to filter for relevant topics.
2. Search for related issues that cover what you're going to work on. It could help to mention there that you will work on the issue. 2. Search for related issues that cover what you're going to work on. It could help to mention there that you will work on the issue.
Pull requests addressing issues labeled ["idea approved"](https://github.com/NixOS/nix/labels/idea%20approved) are especially welcomed by maintainers and will receive prioritised review.
3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests. 3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests.

View file

@ -1,8 +1,12 @@
builtinsDump: let
inherit (builtins) concatStringsSep attrNames;
in
builtinsInfo:
let let
showBuiltin = name: showBuiltin = name:
let let
inherit (builtinsDump.${name}) doc args; inherit (builtinsInfo.${name}) doc args;
in in
'' ''
<dt id="builtins-${name}"> <dt id="builtins-${name}">
@ -14,7 +18,7 @@ let
</dd> </dd>
''; '';
listArgs = args: builtins.concatStringsSep " " (map (s: "<var>${s}</var>") args); listArgs = args: concatStringsSep " " (map (s: "<var>${s}</var>") args);
in in
with builtins; concatStringsSep "\n" (map showBuiltin (attrNames builtinsDump)) concatStringsSep "\n" (map showBuiltin (attrNames builtinsInfo))

View file

@ -1,10 +1,16 @@
cliDumpStr: let
inherit (builtins)
attrNames attrValues fromJSON listToAttrs mapAttrs
concatStringsSep concatMap length lessThan replaceStrings sort;
inherit (import ./utils.nix) concatStrings optionalString filterAttrs trim squash unique showSettings;
in
with builtins; commandDump:
with import ./utils.nix;
let let
commandInfo = fromJSON commandDump;
showCommand = { command, details, filename, toplevel }: showCommand = { command, details, filename, toplevel }:
let let
@ -96,7 +102,7 @@ let
${option.description} ${option.description}
''; '';
categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues allOptions))); categories = sort lessThan (unique (map (cmd: cmd.category) (attrValues allOptions)));
in concatStrings (map showCategory categories); in concatStrings (map showCategory categories);
in squash result; in squash result;
@ -117,13 +123,11 @@ let
}; };
in [ cmd ] ++ concatMap subcommand (attrNames details.commands or {}); in [ cmd ] ++ concatMap subcommand (attrNames details.commands or {});
cliDump = builtins.fromJSON cliDumpStr;
manpages = processCommand { manpages = processCommand {
command = "nix"; command = "nix";
details = cliDump.args; details = commandInfo.args;
filename = "nix"; filename = "nix";
toplevel = cliDump.args; toplevel = commandInfo.args;
}; };
tableOfContents = let tableOfContents = let
@ -143,6 +147,6 @@ let
${showSettings { useAnchors = false; } settings} ${showSettings { useAnchors = false; } settings}
''; '';
in concatStrings (attrValues (mapAttrs showStore cliDump.stores)); in concatStrings (attrValues (mapAttrs showStore commandInfo.stores));
in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; } in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; }

View file

@ -11,6 +11,7 @@ man-pages := $(foreach n, \
nix-prefetch-url.1 nix-channel.1 \ nix-prefetch-url.1 nix-channel.1 \
nix-hash.1 nix-copy-closure.1 \ nix-hash.1 nix-copy-closure.1 \
nix.conf.5 nix-daemon.8 \ nix.conf.5 nix-daemon.8 \
nix-profiles.5 \
, $(d)/$(n)) , $(d)/$(n))
# man pages for subcommands # man pages for subcommands
@ -85,6 +86,12 @@ $(d)/nix.conf.5: $(d)/src/command-ref/conf-file.md
$(trace-gen) lowdown -sT man --nroff-nolinks -M section=5 $^.tmp -o $@ $(trace-gen) lowdown -sT man --nroff-nolinks -M section=5 $^.tmp -o $@
@rm $^.tmp @rm $^.tmp
$(d)/nix-profiles.5: $(d)/src/command-ref/files/profiles.md
@printf "Title: %s\n\n" "$$(basename $@ .5)" > $^.tmp
@cat $^ >> $^.tmp
$(trace-gen) lowdown -sT man --nroff-nolinks -M section=5 $^.tmp -o $@
@rm $^.tmp
$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md
@cp $< $@ @cp $< $@
@$(call process-includes,$@,$@) @$(call process-includes,$@,$@)

View file

@ -338,6 +338,9 @@ const redirects = {
"strings": "#string", "strings": "#string",
"lists": "#list", "lists": "#list",
"attribute-sets": "#attribute-set" "attribute-sets": "#attribute-set"
},
"installation/installing-binary.html": {
"uninstalling": "uninstall.html"
} }
}; };

View file

@ -15,6 +15,7 @@
- [Multi-User Mode](installation/multi-user.md) - [Multi-User Mode](installation/multi-user.md)
- [Environment Variables](installation/env-variables.md) - [Environment Variables](installation/env-variables.md)
- [Upgrading Nix](installation/upgrading.md) - [Upgrading Nix](installation/upgrading.md)
- [Uninstalling Nix](installation/uninstall.md)
- [Package Management](package-management/package-management.md) - [Package Management](package-management/package-management.md)
- [Basic Package Management](package-management/basic-package-mgmt.md) - [Basic Package Management](package-management/basic-package-mgmt.md)
- [Profiles](package-management/profiles.md) - [Profiles](package-management/profiles.md)
@ -91,6 +92,11 @@
{{#include ./command-ref/new-cli/SUMMARY.md}} {{#include ./command-ref/new-cli/SUMMARY.md}}
- [Files](command-ref/files.md) - [Files](command-ref/files.md)
- [nix.conf](command-ref/conf-file.md) - [nix.conf](command-ref/conf-file.md)
- [Profiles](command-ref/files/profiles.md)
- [manifest.nix](command-ref/files/manifest.nix.md)
- [manifest.json](command-ref/files/manifest.json.md)
- [Channels](command-ref/files/channels.md)
- [Default Nix expression](command-ref/files/default-nix-expression.md)
- [Architecture](architecture/architecture.md) - [Architecture](architecture/architecture.md)
- [Glossary](glossary.md) - [Glossary](glossary.md)
- [Contributing](contributing/contributing.md) - [Contributing](contributing/contributing.md)
@ -99,6 +105,7 @@
- [CLI guideline](contributing/cli-guideline.md) - [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md) - [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md) - [Release X.Y (202?-??-??)](release-notes/rl-next.md)
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)
- [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md) - [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md)
- [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md) - [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md)
- [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md) - [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md)

View file

@ -48,13 +48,13 @@ If the build passes and is deterministic, Nix will exit with a status
code of 0: code of 0:
```console ```console
$ nix-build ./deterministic.nix -A stable $ nix-build ./deterministic.nix --attr stable
this derivation will be built: this derivation will be built:
/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv /nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv
building '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'... building '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable /nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
$ nix-build ./deterministic.nix -A stable --check $ nix-build ./deterministic.nix --attr stable --check
checking outputs of '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'... checking outputs of '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable /nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
``` ```
@ -63,13 +63,13 @@ If the build is not deterministic, Nix will exit with a status code of
1: 1:
```console ```console
$ nix-build ./deterministic.nix -A unstable $ nix-build ./deterministic.nix --attr unstable
this derivation will be built: this derivation will be built:
/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv /nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv
building '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'... building '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable /nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable
$ nix-build ./deterministic.nix -A unstable --check $ nix-build ./deterministic.nix --attr unstable --check
checking outputs of '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'... checking outputs of '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
error: derivation '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv' may error: derivation '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv' may
not be deterministic: output '/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable' differs not be deterministic: output '/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable' differs
@ -89,7 +89,7 @@ Using `--check` with `--keep-failed` will cause Nix to keep the second
build's output in a special, `.check` path: build's output in a special, `.check` path:
```console ```console
$ nix-build ./deterministic.nix -A unstable --check --keep-failed $ nix-build ./deterministic.nix --attr unstable --check --keep-failed
checking outputs of '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'... checking outputs of '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
note: keeping build directory '/tmp/nix-build-unstable.drv-0' note: keeping build directory '/tmp/nix-build-unstable.drv-0'
error: derivation '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv' may error: derivation '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv' may

View file

@ -38,11 +38,9 @@ contains Nix.
> **Warning** > **Warning**
> >
> If you are building via the Nix daemon, it is the Nix daemon user > If you are building via the Nix daemon, it is the Nix daemon user account (that is, `root`) that should have SSH access to a user (not necessarily `root`) on the remote machine.
> account (that is, `root`) that should have SSH access to the remote >
> machine. If you cant or dont want to configure `root` to be able to > If you cant or dont want to configure `root` to be able to access the remote machine, you can use a private Nix store instead by passing e.g. `--store ~/my-nix` when running a Nix command from the local machine.
> access to remote machine, you can use a private Nix store instead by
> passing e.g. `--store ~/my-nix`.
The list of remote machines can be specified on the command line or in The list of remote machines can be specified on the command line or in
the Nix configuration file. The former is convenient for testing. For the Nix configuration file. The former is convenient for testing. For

View file

@ -90,7 +90,7 @@ Then, restart the `nix-daemon`.
Build any derivation, for example: Build any derivation, for example:
```console ```console
$ nix-build -E '(import <nixpkgs> {}).writeText "example" (builtins.toString builtins.currentTime)' $ nix-build --expr '(import <nixpkgs> {}).writeText "example" (builtins.toString builtins.currentTime)'
this derivation will be built: this derivation will be built:
/nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv /nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv
building '/nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv'... building '/nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv'...

View file

@ -71,9 +71,12 @@ Most Nix commands interpret the following environment variables:
Settings are separated by the newline character. Settings are separated by the newline character.
- <span id="env-NIX_USER_CONF_FILES">[`NIX_USER_CONF_FILES`](#env-NIX_USER_CONF_FILES)</span>\ - <span id="env-NIX_USER_CONF_FILES">[`NIX_USER_CONF_FILES`](#env-NIX_USER_CONF_FILES)</span>\
Overrides the location of the user Nix configuration files to load Overrides the location of the Nix user configuration files to load from.
from (defaults to the XDG spec locations). The variable is treated
as a list separated by the `:` token. The default are the locations according to the [XDG Base Directory Specification].
See the [XDG Base Directories](#xdg-base-directories) sub-section for details.
The variable is treated as a list separated by the `:` token.
- <span id="env-TMPDIR">[`TMPDIR`](#env-TMPDIR)</span>\ - <span id="env-TMPDIR">[`TMPDIR`](#env-TMPDIR)</span>\
Use the specified directory to store temporary files. In particular, Use the specified directory to store temporary files. In particular,
@ -103,15 +106,19 @@ Most Nix commands interpret the following environment variables:
384 MiB. Setting it to a low value reduces memory consumption, but 384 MiB. Setting it to a low value reduces memory consumption, but
will increase runtime due to the overhead of garbage collection. will increase runtime due to the overhead of garbage collection.
## XDG Base Directory ## XDG Base Directories
New Nix commands conform to the [XDG Base Directory Specification], and use the following environment variables to determine locations of various state and configuration files: Nix follows the [XDG Base Directory Specification].
For backwards compatibility, Nix commands will follow the standard only when [`use-xdg-base-directories`] is enabled.
[New Nix commands](@docroot@/command-ref/new-cli/nix.md) (experimental) conform to the standard by default.
The following environment variables are used to determine locations of various state and configuration files:
- [`XDG_CONFIG_HOME`]{#env-XDG_CONFIG_HOME} (default `~/.config`) - [`XDG_CONFIG_HOME`]{#env-XDG_CONFIG_HOME} (default `~/.config`)
- [`XDG_STATE_HOME`]{#env-XDG_STATE_HOME} (default `~/.local/state`) - [`XDG_STATE_HOME`]{#env-XDG_STATE_HOME} (default `~/.local/state`)
- [`XDG_CACHE_HOME`]{#env-XDG_CACHE_HOME} (default `~/.cache`) - [`XDG_CACHE_HOME`]{#env-XDG_CACHE_HOME} (default `~/.cache`)
Classic Nix commands can also be made to follow this standard using the [`use-xdg-base-directories`] configuration option.
[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html [XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
[`use-xdg-base-directories`]: @docroot@/command-ref/conf-file.md#conf-use-xdg-base-directories [`use-xdg-base-directories`]: @docroot@/command-ref/conf-file.md#conf-use-xdg-base-directories

View file

@ -0,0 +1,26 @@
## Channels
A directory containing symlinks to Nix channels, managed by [`nix-channel`]:
- `$XDG_STATE_HOME/nix/profiles/channels` for regular users
- `$NIX_STATE_DIR/profiles/per-user/root/channels` for `root`
[`nix-channel`] uses a [profile](@docroot@/command-ref/files/profiles.md) to store channels.
This profile contains symlinks to the contents of those channels.
## Subscribed channels
The list of subscribed channels is stored in
- `~/.nix-channels`
- `$XDG_STATE_HOME/nix/channels` if [`use-xdg-base-directories`] is set to `true`
in the following format:
```
<url> <name>
...
```
[`nix-channel`]: @docroot@/command-ref/nix-channel.md
[`use-xdg-base-directories`]: @docroot@/command-ref/conf-file.md#conf-use-xdg-base-directories

View file

@ -0,0 +1,52 @@
## Default Nix expression
The source for the default [Nix expressions](@docroot@/language/index.md) used by [`nix-env`]:
- `~/.nix-defexpr`
- `$XDG_STATE_HOME/nix/defexpr` if [`use-xdg-base-directories`] is set to `true`.
It is loaded as follows:
- If the default expression is a file, it is loaded as a Nix expression.
- If the default expression is a directory containing a `default.nix` file, that `default.nix` file is loaded as a Nix expression.
- If the default expression is a directory without a `default.nix` file, then its contents (both files and subdirectories) are loaded as Nix expressions.
The expressions are combined into a single attribute set, each expression under an attribute with the same name as the original file or subdirectory.
Subdirectories without a `default.nix` file are traversed recursively in search of more Nix expressions, but the names of these intermediate directories are not added to the attribute paths of the default Nix expression.
Then, the resulting expression is interpreted like this:
- If the expression is an attribute set, it is used as the default Nix expression.
- If the expression is a function, an empty set is passed as argument and the return value is used as the default Nix expression.
For example, if the default expression contains two files, `foo.nix` and `bar.nix`, then the default Nix expression will be equivalent to
```nix
{
foo = import ~/.nix-defexpr/foo.nix;
bar = import ~/.nix-defexpr/bar.nix;
}
```
The file [`manifest.nix`](@docroot@/command-ref/files/manifest.nix.md) is always ignored.
The command [`nix-channel`] places a symlink to the user's current [channels profile](@docroot@/command-ref/files/channels.md) in this directory.
This makes all subscribed channels available as attributes in the default expression.
## User channel link
A symlink that ensures that [`nix-env`] can find your channels:
- `~/.nix-defexpr/channels`
- `$XDG_STATE_HOME/defexpr/channels` if [`use-xdg-base-directories`] is set to `true`.
This symlink points to:
- `$XDG_STATE_HOME/profiles/channels` for regular users
- `$NIX_STATE_DIR/profiles/per-user/root/channels` for `root`
In a multi-user installation, you may also have `~/.nix-defexpr/channels_root`, which links to the channels of the root user.[`nix-env`]: ../nix-env.md
[`nix-env`]: @docroot@/command-ref/nix-env.md
[`nix-channel`]: @docroot@/command-ref/nix-channel.md
[`use-xdg-base-directories`]: @docroot@/command-ref/conf-file.md#conf-use-xdg-base-directories

View file

@ -0,0 +1,45 @@
## `manifest.json`
The manifest file records the provenance of the packages that are installed in a [profile](./profiles.md) managed by [`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md) (experimental).
Here is an example of what the file might look like after installing `zoom-us` from Nixpkgs:
```json
{
"version": 1,
"elements": [
{
"active": true,
"attrPath": "legacyPackages.x86_64-linux.zoom-us",
"originalUrl": "flake:nixpkgs",
"storePaths": [
"/nix/store/wbhg2ga8f3h87s9h5k0slxk0m81m4cxl-zoom-us-5.3.469451.0927"
],
"uri": "github:NixOS/nixpkgs/13d0c311e3ae923a00f734b43fd1d35b47d8943a"
},
]
}
```
Each object in the array `elements` denotes an installed package and
has the following fields:
* `originalUrl`: The [flake reference](@docroot@/command-ref/new-cli/nix3-flake.md) specified by
the user at the time of installation (e.g. `nixpkgs`). This is also
the flake reference that will be used by `nix profile upgrade`.
* `uri`: The locked flake reference to which `originalUrl` resolved.
* `attrPath`: The flake output attribute that provided this
package. Note that this is not necessarily the attribute that the
user specified, but the one resulting from applying the default
attribute paths and prefixes; for instance, `hello` might resolve to
`packages.x86_64-linux.hello` and the empty string to
`packages.x86_64-linux.default`.
* `storePath`: The paths in the Nix store containing the package.
* `active`: Whether the profile contains symlinks to the files of this
package. If set to false, the package is kept in the Nix store, but
is not "visible" in the profile's symlink tree.

View file

@ -0,0 +1,128 @@
## `manifest.nix`
The manifest file records the provenance of the packages that are installed in a [profile](./profiles.md) managed by [`nix-env`](@docroot@/command-ref/nix-env.md).
Here is an example of how this file might look like after installing `hello` from Nixpkgs:
```nix
[{
meta = {
available = true;
broken = false;
changelog =
"https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v2.12.1";
description = "A program that produces a familiar, friendly greeting";
homepage = "https://www.gnu.org/software/hello/manual/";
insecure = false;
license = {
deprecated = false;
free = true;
fullName = "GNU General Public License v3.0 or later";
redistributable = true;
shortName = "gpl3Plus";
spdxId = "GPL-3.0-or-later";
url = "https://spdx.org/licenses/GPL-3.0-or-later.html";
};
longDescription = ''
GNU Hello is a program that prints "Hello, world!" when you run it.
It is fully customizable.
'';
maintainers = [{
email = "edolstra+nixpkgs@gmail.com";
github = "edolstra";
githubId = 1148549;
name = "Eelco Dolstra";
}];
name = "hello-2.12.1";
outputsToInstall = [ "out" ];
platforms = [
"i686-cygwin"
"x86_64-cygwin"
"x86_64-darwin"
"i686-darwin"
"aarch64-darwin"
"armv7a-darwin"
"i686-freebsd13"
"x86_64-freebsd13"
"aarch64-genode"
"i686-genode"
"x86_64-genode"
"x86_64-solaris"
"js-ghcjs"
"aarch64-linux"
"armv5tel-linux"
"armv6l-linux"
"armv7a-linux"
"armv7l-linux"
"i686-linux"
"m68k-linux"
"microblaze-linux"
"microblazeel-linux"
"mipsel-linux"
"mips64el-linux"
"powerpc64-linux"
"powerpc64le-linux"
"riscv32-linux"
"riscv64-linux"
"s390-linux"
"s390x-linux"
"x86_64-linux"
"mmix-mmixware"
"aarch64-netbsd"
"armv6l-netbsd"
"armv7a-netbsd"
"armv7l-netbsd"
"i686-netbsd"
"m68k-netbsd"
"mipsel-netbsd"
"powerpc-netbsd"
"riscv32-netbsd"
"riscv64-netbsd"
"x86_64-netbsd"
"aarch64_be-none"
"aarch64-none"
"arm-none"
"armv6l-none"
"avr-none"
"i686-none"
"microblaze-none"
"microblazeel-none"
"msp430-none"
"or1k-none"
"m68k-none"
"powerpc-none"
"powerpcle-none"
"riscv32-none"
"riscv64-none"
"rx-none"
"s390-none"
"s390x-none"
"vc4-none"
"x86_64-none"
"i686-openbsd"
"x86_64-openbsd"
"x86_64-redox"
"wasm64-wasi"
"wasm32-wasi"
"x86_64-windows"
"i686-windows"
];
position =
"/nix/store/7niq32w715567hbph0q13m5lqna64c1s-nixos-unstable.tar.gz/nixos-unstable.tar.gz/pkgs/applications/misc/hello/default.nix:34";
unfree = false;
unsupported = false;
};
name = "hello-2.12.1";
out = {
outPath = "/nix/store/260q5867crm1xjs4khgqpl6vr9kywql1-hello-2.12.1";
};
outPath = "/nix/store/260q5867crm1xjs4khgqpl6vr9kywql1-hello-2.12.1";
outputs = [ "out" ];
system = "x86_64-linux";
type = "derivation";
}]
```
Each element in this list corresponds to an installed package.
It incorporates some attributes of the original derivation, including `meta`, `name`, `out`, `outPath`, `outputs`, `system`.
This information is used by Nix for querying and updating the package.

View file

@ -0,0 +1,74 @@
## Profiles
A directory that contains links to profiles managed by [`nix-env`] and [`nix profile`]:
- `$XDG_STATE_HOME/nix/profiles` for regular users
- `$NIX_STATE_DIR/profiles/per-user/root` if the user is `root`
A profile is a directory of symlinks to files in the Nix store.
### Filesystem layout
Profiles are versioned as follows. When using a profile named *path*, *path* is a symlink to *path*`-`*N*`-link`, where *N* is the version of the profile.
In turn, *path*`-`*N*`-link` is a symlink to a path in the Nix store.
For example:
```console
$ ls -l ~alice/.local/state/nix/profiles/profile*
lrwxrwxrwx 1 alice users 14 Nov 25 14:35 /home/alice/.local/state/nix/profiles/profile -> profile-7-link
lrwxrwxrwx 1 alice users 51 Oct 28 16:18 /home/alice/.local/state/nix/profiles/profile-5-link -> /nix/store/q69xad13ghpf7ir87h0b2gd28lafjj1j-profile
lrwxrwxrwx 1 alice users 51 Oct 29 13:20 /home/alice/.local/state/nix/profiles/profile-6-link -> /nix/store/6bvhpysd7vwz7k3b0pndn7ifi5xr32dg-profile
lrwxrwxrwx 1 alice users 51 Nov 25 14:35 /home/alice/.local/state/nix/profiles/profile-7-link -> /nix/store/mp0x6xnsg0b8qhswy6riqvimai4gm677-profile
```
Each of these symlinks is a root for the Nix garbage collector.
The contents of the store path corresponding to each version of the
profile is a tree of symlinks to the files of the installed packages,
e.g.
```console
$ ll -R ~eelco/.local/state/nix/profiles/profile-7-link/
/home/eelco/.local/state/nix/profiles/profile-7-link/:
total 20
dr-xr-xr-x 2 root root 4096 Jan 1 1970 bin
-r--r--r-- 2 root root 1402 Jan 1 1970 manifest.nix
dr-xr-xr-x 4 root root 4096 Jan 1 1970 share
/home/eelco/.local/state/nix/profiles/profile-7-link/bin:
total 20
lrwxrwxrwx 5 root root 79 Jan 1 1970 chromium -> /nix/store/ijm5k0zqisvkdwjkc77mb9qzb35xfi4m-chromium-86.0.4240.111/bin/chromium
lrwxrwxrwx 7 root root 87 Jan 1 1970 spotify -> /nix/store/w9182874m1bl56smps3m5zjj36jhp3rn-spotify-1.1.26.501.gbe11e53b-15/bin/spotify
lrwxrwxrwx 3 root root 79 Jan 1 1970 zoom-us -> /nix/store/wbhg2ga8f3h87s9h5k0slxk0m81m4cxl-zoom-us-5.3.469451.0927/bin/zoom-us
/home/eelco/.local/state/nix/profiles/profile-7-link/share/applications:
total 12
lrwxrwxrwx 4 root root 120 Jan 1 1970 chromium-browser.desktop -> /nix/store/4cf803y4vzfm3gyk3vzhzb2327v0kl8a-chromium-unwrapped-86.0.4240.111/share/applications/chromium-browser.desktop
lrwxrwxrwx 7 root root 110 Jan 1 1970 spotify.desktop -> /nix/store/w9182874m1bl56smps3m5zjj36jhp3rn-spotify-1.1.26.501.gbe11e53b-15/share/applications/spotify.desktop
lrwxrwxrwx 3 root root 107 Jan 1 1970 us.zoom.Zoom.desktop -> /nix/store/wbhg2ga8f3h87s9h5k0slxk0m81m4cxl-zoom-us-5.3.469451.0927/share/applications/us.zoom.Zoom.desktop
```
Each profile version contains a manifest file:
- [`manifest.nix`](@docroot@/command-ref/files/manifest.nix.md) used by [`nix-env`](@docroot@/command-ref/nix-env.md).
- [`manifest.json`](@docroot@/command-ref/files/manifest.json.md) used by [`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md) (experimental).
## User profile link
A symbolic link to the user's current profile:
- `~/.nix-profile`
- `$XDG_STATE_HOME/nix/profile` if [`use-xdg-base-directories`] is set to `true`.
By default, this symlink points to:
- `$XDG_STATE_HOME/nix/profiles/profile` for regular users
- `$NIX_STATE_DIR/profiles/per-user/root/profile` for `root`
The `PATH` environment variable should include `/bin` subdirectory of the profile link (e.g. `~/.nix-profile/bin`) for the user environment to be visible to the user.
The [installer](@docroot@/installation/installing-binary.md) sets this up by default, unless you enable [`use-xdg-base-directories`].
[`nix-env`]: @docroot@/command-ref/nix-env.md
[`nix profile`]: @docroot@/command-ref/new-cli/nix3-profile.md
[`use-xdg-base-directories`]: @docroot@/command-ref/conf-file.md#conf-use-xdg-base-directories

View file

@ -76,7 +76,7 @@ except for `--arg` and `--attr` / `-A` which are passed to `nix-instantiate`.
# Examples # Examples
```console ```console
$ nix-build '<nixpkgs>' -A firefox $ nix-build '<nixpkgs>' --attr firefox
store derivation is /nix/store/qybprl8sz2lc...-firefox-1.5.0.7.drv store derivation is /nix/store/qybprl8sz2lc...-firefox-1.5.0.7.drv
/nix/store/d18hyl92g30l...-firefox-1.5.0.7 /nix/store/d18hyl92g30l...-firefox-1.5.0.7
@ -91,7 +91,7 @@ If a derivation has multiple outputs, `nix-build` will build the default
(first) output. You can also build all outputs: (first) output. You can also build all outputs:
```console ```console
$ nix-build '<nixpkgs>' -A openssl.all $ nix-build '<nixpkgs>' --attr openssl.all
``` ```
This will create a symlink for each output named `result-outputname`. This will create a symlink for each output named `result-outputname`.
@ -101,7 +101,7 @@ outputs `out`, `bin` and `man`, `nix-build` will create symlinks
specific output: specific output:
```console ```console
$ nix-build '<nixpkgs>' -A openssl.man $ nix-build '<nixpkgs>' --attr openssl.man
``` ```
This will create a symlink `result-man`. This will create a symlink `result-man`.
@ -109,7 +109,7 @@ This will create a symlink `result-man`.
Build a Nix expression given on the command line: Build a Nix expression given on the command line:
```console ```console
$ nix-build -E 'with import <nixpkgs> { }; runCommand "foo" { } "echo bar > $out"' $ nix-build --expr 'with import <nixpkgs> { }; runCommand "foo" { } "echo bar > $out"'
$ cat ./result $ cat ./result
bar bar
``` ```
@ -118,5 +118,5 @@ Build the GNU Hello package from the latest revision of the master
branch of Nixpkgs: branch of Nixpkgs:
```console ```console
$ nix-build https://github.com/NixOS/nixpkgs/archive/master.tar.gz -A hello $ nix-build https://github.com/NixOS/nixpkgs/archive/master.tar.gz --attr hello
``` ```

View file

@ -22,6 +22,9 @@ This command has the following operations:
channels. If *name* is omitted, it defaults to the last component of channels. If *name* is omitted, it defaults to the last component of
*url*, with the suffixes `-stable` or `-unstable` removed. *url*, with the suffixes `-stable` or `-unstable` removed.
A channel URL must point to a directory containing a file `nixexprs.tar.gz`.
At the top level, that tarball must contain a single directory with a `default.nix` file that serves as the channels entry point.
- `--remove` *name*\ - `--remove` *name*\
Removes the channel named *name* from the list of subscribed Removes the channel named *name* from the list of subscribed
channels. channels.
@ -49,6 +52,12 @@ The list of subscribed channels is stored in `~/.nix-channels`.
{{#include ./env-common.md}} {{#include ./env-common.md}}
# Files
`nix-channel` operates on the following files.
{{#include ./files/channels.md}}
# Examples # Examples
To subscribe to the Nixpkgs channel and install the GNU Hello package: To subscribe to the Nixpkgs channel and install the GNU Hello package:
@ -56,45 +65,18 @@ To subscribe to the Nixpkgs channel and install the GNU Hello package:
```console ```console
$ nix-channel --add https://nixos.org/channels/nixpkgs-unstable $ nix-channel --add https://nixos.org/channels/nixpkgs-unstable
$ nix-channel --update $ nix-channel --update
$ nix-env -iA nixpkgs.hello $ nix-env --install --attr nixpkgs.hello
``` ```
You can revert channel updates using `--rollback`: You can revert channel updates using `--rollback`:
```console ```console
$ nix-instantiate --eval -E '(import <nixpkgs> {}).lib.version' $ nix-instantiate --eval --expr '(import <nixpkgs> {}).lib.version'
"14.04.527.0e935f1" "14.04.527.0e935f1"
$ nix-channel --rollback $ nix-channel --rollback
switching from generation 483 to 482 switching from generation 483 to 482
$ nix-instantiate --eval -E '(import <nixpkgs> {}).lib.version' $ nix-instantiate --eval --expr '(import <nixpkgs> {}).lib.version'
"14.04.526.dbadfad" "14.04.526.dbadfad"
``` ```
# Files
- `${XDG_STATE_HOME-$HOME/.local/state}/nix/profiles/channels`\
`nix-channel` uses a `nix-env` profile to keep track of previous
versions of the subscribed channels. Every time you run `nix-channel
--update`, a new channel generation (that is, a symlink to the
channel Nix expressions in the Nix store) is created. This enables
`nix-channel --rollback` to revert to previous versions.
- `~/.nix-defexpr/channels`\
This is a symlink to
`${XDG_STATE_HOME-$HOME/.local/state}/nix/profiles/channels`. It ensures that
`nix-env` can find your channels. In a multi-user installation, you
may also have `~/.nix-defexpr/channels_root`, which links to the
channels of the root user.
# Channel format
A channel URL should point to a directory containing the following
files:
- `nixexprs.tar.xz`\
A tarball containing Nix expressions and files referenced by them
(such as build scripts and patches). At the top level, the tarball
should contain a single directory. That directory must contain a
file `default.nix` that serves as the channels “entry point”.

View file

@ -87,5 +87,5 @@ environment:
```console ```console
$ nix-copy-closure --from alice@itchy.labs \ $ nix-copy-closure --from alice@itchy.labs \
/nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4
$ nix-env -i /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 $ nix-env --install /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4
``` ```

View file

@ -49,7 +49,7 @@ These pages can be viewed offline:
# Selectors # Selectors
Several commands, such as `nix-env -q` and `nix-env -i`, take a list of Several commands, such as `nix-env --query ` and `nix-env --install `, take a list of
arguments that specify the packages on which to operate. These are arguments that specify the packages on which to operate. These are
extended regular expressions that must match the entire name of the extended regular expressions that must match the entire name of the
package. (For details on regular expressions, see **regex**(7).) The match is package. (For details on regular expressions, see **regex**(7).) The match is
@ -83,46 +83,8 @@ match. Here are some examples:
# Files # Files
- `~/.nix-defexpr`\ `nix-env` operates on the following files.
The source for the default Nix expressions used by the
`--install`, `--upgrade`, and `--query --available` operations to
obtain derivations. The `--file` option may be used to override
this default.
If `~/.nix-defexpr` is a file, it is loaded as a Nix expression. If {{#include ./files/default-nix-expression.md}}
the expression is a set, it is used as the default Nix expression.
If the expression is a function, an empty set is passed as argument
and the return value is used as the default Nix expression.
If `~/.nix-defexpr` is a directory containing a `default.nix` file, {{#include ./files/profiles.md}}
that file is loaded as in the above paragraph.
If `~/.nix-defexpr` is a directory without a `default.nix` file,
then its contents (both files and subdirectories) are loaded as Nix
expressions. The expressions are combined into a single set, each
expression under an attribute with the same name as the original
file or subdirectory.
For example, if `~/.nix-defexpr` contains two files, `foo.nix` and
`bar.nix`, then the default Nix expression will essentially be
```nix
{
foo = import ~/.nix-defexpr/foo.nix;
bar = import ~/.nix-defexpr/bar.nix;
}
```
The file `manifest.nix` is always ignored. Subdirectories without a
`default.nix` file are traversed recursively in search of more Nix
expressions, but the names of these intermediate directories are not
added to the attribute paths of the default Nix expression.
The command `nix-channel` places symlinks to the downloaded Nix
expressions from each subscribed channel in this directory.
- `~/.nix-profile`\
A symbolic link to the user's current profile. By default, this
symlink points to `prefix/var/nix/profiles/default`. The `PATH`
environment variable should include `~/.nix-profile/bin` for the
user environment to be visible to the user.

View file

@ -41,6 +41,6 @@ $ nix-env --delete-generations 30d
``` ```
```console ```console
$ nix-env -p other_profile --delete-generations old $ nix-env --profile other_profile --delete-generations old
``` ```

View file

@ -36,7 +36,7 @@ a number of possible ways:
then the derivation with the highest version will be installed. then the derivation with the highest version will be installed.
You can force the installation of multiple derivations with the same You can force the installation of multiple derivations with the same
name by being specific about the versions. For instance, `nix-env -i name by being specific about the versions. For instance, `nix-env --install
gcc-3.3.6 gcc-4.1.1` will install both version of GCC (and will gcc-3.3.6 gcc-4.1.1` will install both version of GCC (and will
probably cause a user environment conflict\!). probably cause a user environment conflict\!).
@ -44,7 +44,7 @@ a number of possible ways:
paths* that select attributes from the top-level Nix paths* that select attributes from the top-level Nix
expression. This is faster than using derivation names and expression. This is faster than using derivation names and
unambiguous. To find out the attribute paths of available unambiguous. To find out the attribute paths of available
packages, use `nix-env -qaP`. packages, use `nix-env --query --available --attr-path `.
- If `--from-profile` *path* is given, *args* is a set of names - If `--from-profile` *path* is given, *args* is a set of names
denoting installed store paths in the profile *path*. This is an denoting installed store paths in the profile *path*. This is an
@ -87,7 +87,7 @@ a number of possible ways:
- `--remove-all` / `-r`\ - `--remove-all` / `-r`\
Remove all previously installed packages first. This is equivalent Remove all previously installed packages first. This is equivalent
to running `nix-env -e '.*'` first, except that everything happens to running `nix-env --uninstall '.*'` first, except that everything happens
in a single transaction. in a single transaction.
{{#include ./opt-common.md}} {{#include ./opt-common.md}}
@ -103,9 +103,9 @@ a number of possible ways:
To install a package using a specific attribute path from the active Nix expression: To install a package using a specific attribute path from the active Nix expression:
```console ```console
$ nix-env -iA gcc40mips $ nix-env --install --attr gcc40mips
installing `gcc-4.0.2' installing `gcc-4.0.2'
$ nix-env -iA xorg.xorgserver $ nix-env --install --attr xorg.xorgserver
installing `xorg-server-1.2.0' installing `xorg-server-1.2.0'
``` ```
@ -133,32 +133,32 @@ installing `gcc-3.3.2'
To install all derivations in the Nix expression `foo.nix`: To install all derivations in the Nix expression `foo.nix`:
```console ```console
$ nix-env -f ~/foo.nix -i '.*' $ nix-env --file ~/foo.nix --install '.*'
``` ```
To copy the store path with symbolic name `gcc` from another profile: To copy the store path with symbolic name `gcc` from another profile:
```console ```console
$ nix-env -i --from-profile /nix/var/nix/profiles/foo gcc $ nix-env --install --from-profile /nix/var/nix/profiles/foo gcc
``` ```
To install a specific [store derivation] (typically created by To install a specific [store derivation] (typically created by
`nix-instantiate`): `nix-instantiate`):
```console ```console
$ nix-env -i /nix/store/fibjb1bfbpm5mrsxc4mh2d8n37sxh91i-gcc-3.4.3.drv $ nix-env --install /nix/store/fibjb1bfbpm5mrsxc4mh2d8n37sxh91i-gcc-3.4.3.drv
``` ```
To install a specific output path: To install a specific output path:
```console ```console
$ nix-env -i /nix/store/y3cgx0xj1p4iv9x0pnnmdhr8iyg741vk-gcc-3.4.3 $ nix-env --install /nix/store/y3cgx0xj1p4iv9x0pnnmdhr8iyg741vk-gcc-3.4.3
``` ```
To install from a Nix expression specified on the command-line: To install from a Nix expression specified on the command-line:
```console ```console
$ nix-env -f ./foo.nix -i -E \ $ nix-env --file ./foo.nix --install --expr \
'f: (f {system = "i686-linux";}).subversionWithJava' 'f: (f {system = "i686-linux";}).subversionWithJava'
``` ```
@ -170,7 +170,7 @@ function defined in `./foo.nix`.
A dry-run tells you which paths will be downloaded or built from source: A dry-run tells you which paths will be downloaded or built from source:
```console ```console
$ nix-env -f '<nixpkgs>' -iA hello --dry-run $ nix-env --file '<nixpkgs>' --install --attr hello --dry-run
(dry run; not doing anything) (dry run; not doing anything)
installing hello-2.10 installing hello-2.10
this path will be fetched (0.04 MiB download, 0.19 MiB unpacked): this path will be fetched (0.04 MiB download, 0.19 MiB unpacked):
@ -182,6 +182,6 @@ To install Firefox from the latest revision in the Nixpkgs/NixOS 14.12
channel: channel:
```console ```console
$ nix-env -f https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz -iA firefox $ nix-env --file https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz --install --attr firefox
``` ```

View file

@ -137,7 +137,7 @@ derivation is shown unless `--no-name` is specified.
To show installed packages: To show installed packages:
```console ```console
$ nix-env -q $ nix-env --query
bison-1.875c bison-1.875c
docbook-xml-4.2 docbook-xml-4.2
firefox-1.0.4 firefox-1.0.4
@ -149,7 +149,7 @@ ORBit2-2.8.3
To show available packages: To show available packages:
```console ```console
$ nix-env -qa $ nix-env --query --available
firefox-1.0.7 firefox-1.0.7
GConf-2.4.0.1 GConf-2.4.0.1
MPlayer-1.0pre7 MPlayer-1.0pre7
@ -160,7 +160,7 @@ ORBit2-2.8.3
To show the status of available packages: To show the status of available packages:
```console ```console
$ nix-env -qas $ nix-env --query --available --status
-P- firefox-1.0.7 (not installed but present) -P- firefox-1.0.7 (not installed but present)
--S GConf-2.4.0.1 (not present, but there is a substitute for fast installation) --S GConf-2.4.0.1 (not present, but there is a substitute for fast installation)
--S MPlayer-1.0pre3 (i.e., this is not the installed MPlayer, even though the version is the same!) --S MPlayer-1.0pre3 (i.e., this is not the installed MPlayer, even though the version is the same!)
@ -171,14 +171,14 @@ IP- ORBit2-2.8.3 (installed and by definition present)
To show available packages in the Nix expression `foo.nix`: To show available packages in the Nix expression `foo.nix`:
```console ```console
$ nix-env -f ./foo.nix -qa $ nix-env --file ./foo.nix --query --available
foo-1.2.3 foo-1.2.3
``` ```
To compare installed versions to whats available: To compare installed versions to whats available:
```console ```console
$ nix-env -qc $ nix-env --query --compare-versions
... ...
acrobat-reader-7.0 - ? (package is not available at all) acrobat-reader-7.0 - ? (package is not available at all)
autoconf-2.59 = 2.59 (same version) autoconf-2.59 = 2.59 (same version)
@ -189,7 +189,7 @@ firefox-1.0.4 < 1.0.7 (a more recent version is available)
To show all packages with “`zip`” in the name: To show all packages with “`zip`” in the name:
```console ```console
$ nix-env -qa '.*zip.*' $ nix-env --query --available '.*zip.*'
bzip2-1.0.6 bzip2-1.0.6
gzip-1.6 gzip-1.6
zip-3.0 zip-3.0
@ -199,7 +199,7 @@ zip-3.0
To show all packages with “`firefox`” or “`chromium`” in the name: To show all packages with “`firefox`” or “`chromium`” in the name:
```console ```console
$ nix-env -qa '.*(firefox|chromium).*' $ nix-env --query --available '.*(firefox|chromium).*'
chromium-37.0.2062.94 chromium-37.0.2062.94
chromium-beta-38.0.2125.24 chromium-beta-38.0.2125.24
firefox-32.0.3 firefox-32.0.3
@ -210,6 +210,6 @@ firefox-with-plugins-13.0.1
To show all packages in the latest revision of the Nixpkgs repository: To show all packages in the latest revision of the Nixpkgs repository:
```console ```console
$ nix-env -f https://github.com/NixOS/nixpkgs/archive/master.tar.gz -qa $ nix-env --file https://github.com/NixOS/nixpkgs/archive/master.tar.gz --query --available
``` ```

View file

@ -46,16 +46,16 @@ To prevent the currently installed Firefox from being upgraded:
$ nix-env --set-flag keep true firefox $ nix-env --set-flag keep true firefox
``` ```
After this, `nix-env -u` will ignore Firefox. After this, `nix-env --upgrade ` will ignore Firefox.
To disable the currently installed Firefox, then install a new Firefox To disable the currently installed Firefox, then install a new Firefox
while the old remains part of the profile: while the old remains part of the profile:
```console ```console
$ nix-env -q $ nix-env --query
firefox-2.0.0.9 (the current one) firefox-2.0.0.9 (the current one)
$ nix-env --preserve-installed -i firefox-2.0.0.11 $ nix-env --preserve-installed --install firefox-2.0.0.11
installing `firefox-2.0.0.11' installing `firefox-2.0.0.11'
building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment' building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment'
collision between `/nix/store/...-firefox-2.0.0.11/bin/firefox' collision between `/nix/store/...-firefox-2.0.0.11/bin/firefox'
@ -65,10 +65,10 @@ collision between `/nix/store/...-firefox-2.0.0.11/bin/firefox'
$ nix-env --set-flag active false firefox $ nix-env --set-flag active false firefox
setting flag on `firefox-2.0.0.9' setting flag on `firefox-2.0.0.9'
$ nix-env --preserve-installed -i firefox-2.0.0.11 $ nix-env --preserve-installed --install firefox-2.0.0.11
installing `firefox-2.0.0.11' installing `firefox-2.0.0.11'
$ nix-env -q $ nix-env --query
firefox-2.0.0.11 (the enabled one) firefox-2.0.0.11 (the enabled one)
firefox-2.0.0.9 (the disabled one) firefox-2.0.0.9 (the disabled one)
``` ```

View file

@ -25,6 +25,6 @@ The following updates a profile such that its current generation will
contain just Firefox: contain just Firefox:
```console ```console
$ nix-env -p /nix/var/nix/profiles/browser --set firefox $ nix-env --profile /nix/var/nix/profiles/browser --set firefox
``` ```

View file

@ -27,7 +27,7 @@ Switching will fail if the specified generation does not exist.
# Examples # Examples
```console ```console
$ nix-env -G 42 $ nix-env --switch-generation 42
switching from generation 50 to 42 switching from generation 50 to 42
``` ```

View file

@ -22,5 +22,5 @@ the symlink `~/.nix-profile` is made to point to *path*.
# Examples # Examples
```console ```console
$ nix-env -S ~/my-profile $ nix-env --switch-profile ~/my-profile
``` ```

View file

@ -24,5 +24,5 @@ designated by the symbolic names *drvnames* are removed.
```console ```console
$ nix-env --uninstall gcc $ nix-env --uninstall gcc
$ nix-env -e '.*' (remove everything) $ nix-env --uninstall '.*' (remove everything)
``` ```

View file

@ -76,21 +76,21 @@ version is installed.
# Examples # Examples
```console ```console
$ nix-env --upgrade -A nixpkgs.gcc $ nix-env --upgrade --attr nixpkgs.gcc
upgrading `gcc-3.3.1' to `gcc-3.4' upgrading `gcc-3.3.1' to `gcc-3.4'
``` ```
When there are no updates available, nothing will happen: When there are no updates available, nothing will happen:
```console ```console
$ nix-env --upgrade -A nixpkgs.pan $ nix-env --upgrade --attr nixpkgs.pan
``` ```
Using `-A` is preferred when possible, as it is faster and unambiguous but Using `-A` is preferred when possible, as it is faster and unambiguous but
it is also possible to upgrade to a specific version by matching the derivation name: it is also possible to upgrade to a specific version by matching the derivation name:
```console ```console
$ nix-env -u gcc-3.3.2 --always $ nix-env --upgrade gcc-3.3.2 --always
upgrading `gcc-3.4' to `gcc-3.3.2' upgrading `gcc-3.4' to `gcc-3.3.2'
``` ```
@ -98,7 +98,7 @@ To try to upgrade everything
(matching packages based on the part of the derivation name without version): (matching packages based on the part of the derivation name without version):
```console ```console
$ nix-env -u $ nix-env --upgrade
upgrading `hello-2.1.2' to `hello-2.1.3' upgrading `hello-2.1.2' to `hello-2.1.3'
upgrading `mozilla-1.2' to `mozilla-1.4' upgrading `mozilla-1.2' to `mozilla-1.4'
``` ```

View file

@ -88,7 +88,7 @@ Instantiate [store derivation]s from a Nix expression, and build them using `nix
$ nix-instantiate test.nix (instantiate) $ nix-instantiate test.nix (instantiate)
/nix/store/cigxbmvy6dzix98dxxh9b6shg7ar5bvs-perl-BerkeleyDB-0.26.drv /nix/store/cigxbmvy6dzix98dxxh9b6shg7ar5bvs-perl-BerkeleyDB-0.26.drv
$ nix-store -r $(nix-instantiate test.nix) (build) $ nix-store --realise $(nix-instantiate test.nix) (build)
... ...
/nix/store/qhqk4n8ci095g3sdp93x7rgwyh9rdvgk-perl-BerkeleyDB-0.26 (output path) /nix/store/qhqk4n8ci095g3sdp93x7rgwyh9rdvgk-perl-BerkeleyDB-0.26 (output path)
@ -100,30 +100,30 @@ dr-xr-xr-x 2 eelco users 4096 1970-01-01 01:00 lib
You can also give a Nix expression on the command line: You can also give a Nix expression on the command line:
```console ```console
$ nix-instantiate -E 'with import <nixpkgs> { }; hello' $ nix-instantiate --expr 'with import <nixpkgs> { }; hello'
/nix/store/j8s4zyv75a724q38cb0r87rlczaiag4y-hello-2.8.drv /nix/store/j8s4zyv75a724q38cb0r87rlczaiag4y-hello-2.8.drv
``` ```
This is equivalent to: This is equivalent to:
```console ```console
$ nix-instantiate '<nixpkgs>' -A hello $ nix-instantiate '<nixpkgs>' --attr hello
``` ```
Parsing and evaluating Nix expressions: Parsing and evaluating Nix expressions:
```console ```console
$ nix-instantiate --parse -E '1 + 2' $ nix-instantiate --parse --expr '1 + 2'
1 + 2 1 + 2
``` ```
```console ```console
$ nix-instantiate --eval -E '1 + 2' $ nix-instantiate --eval --expr '1 + 2'
3 3
``` ```
```console ```console
$ nix-instantiate --eval --xml -E '1 + 2' $ nix-instantiate --eval --xml --expr '1 + 2'
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<expr> <expr>
<int value="3" /> <int value="3" />
@ -133,7 +133,7 @@ $ nix-instantiate --eval --xml -E '1 + 2'
The difference between non-strict and strict evaluation: The difference between non-strict and strict evaluation:
```console ```console
$ nix-instantiate --eval --xml -E 'rec { x = "foo"; y = x; }' $ nix-instantiate --eval --xml --expr 'rec { x = "foo"; y = x; }'
... ...
<attr name="x"> <attr name="x">
<string value="foo" /> <string value="foo" />
@ -148,7 +148,7 @@ Note that `y` is left unevaluated (the XML representation doesnt
attempt to show non-normal forms). attempt to show non-normal forms).
```console ```console
$ nix-instantiate --eval --xml --strict -E 'rec { x = "foo"; y = x; }' $ nix-instantiate --eval --xml --strict --expr 'rec { x = "foo"; y = x; }'
... ...
<attr name="x"> <attr name="x">
<string value="foo" /> <string value="foo" />

View file

@ -89,7 +89,7 @@ All options not listed here are passed to `nix-store
- `--packages` / `-p` *packages*…\ - `--packages` / `-p` *packages*…\
Set up an environment in which the specified packages are present. Set up an environment in which the specified packages are present.
The command line arguments are interpreted as attribute names inside The command line arguments are interpreted as attribute names inside
the Nix Packages collection. Thus, `nix-shell -p libjpeg openjdk` the Nix Packages collection. Thus, `nix-shell --packages libjpeg openjdk`
will start a shell in which the packages denoted by the attribute will start a shell in which the packages denoted by the attribute
names `libjpeg` and `openjdk` are present. names `libjpeg` and `openjdk` are present.
@ -118,7 +118,7 @@ To build the dependencies of the package Pan, and start an interactive
shell in which to build it: shell in which to build it:
```console ```console
$ nix-shell '<nixpkgs>' -A pan $ nix-shell '<nixpkgs>' --attr pan
[nix-shell]$ eval ${unpackPhase:-unpackPhase} [nix-shell]$ eval ${unpackPhase:-unpackPhase}
[nix-shell]$ cd $sourceRoot [nix-shell]$ cd $sourceRoot
[nix-shell]$ eval ${patchPhase:-patchPhase} [nix-shell]$ eval ${patchPhase:-patchPhase}
@ -137,7 +137,7 @@ To clear the environment first, and do some additional automatic
initialisation of the interactive shell: initialisation of the interactive shell:
```console ```console
$ nix-shell '<nixpkgs>' -A pan --pure \ $ nix-shell '<nixpkgs>' --attr pan --pure \
--command 'export NIX_DEBUG=1; export NIX_CORES=8; return' --command 'export NIX_DEBUG=1; export NIX_CORES=8; return'
``` ```
@ -146,13 +146,13 @@ Nix expressions can also be given on the command line using the `-E` and
packages `sqlite` and `libX11`: packages `sqlite` and `libX11`:
```console ```console
$ nix-shell -E 'with import <nixpkgs> { }; runCommand "dummy" { buildInputs = [ sqlite xorg.libX11 ]; } ""' $ nix-shell --expr 'with import <nixpkgs> { }; runCommand "dummy" { buildInputs = [ sqlite xorg.libX11 ]; } ""'
``` ```
A shorter way to do the same is: A shorter way to do the same is:
```console ```console
$ nix-shell -p sqlite xorg.libX11 $ nix-shell --packages sqlite xorg.libX11
[nix-shell]$ echo $NIX_LDFLAGS [nix-shell]$ echo $NIX_LDFLAGS
… -L/nix/store/j1zg5v…-sqlite-3.8.0.2/lib -L/nix/store/0gmcz9…-libX11-1.6.1/lib … … -L/nix/store/j1zg5v…-sqlite-3.8.0.2/lib -L/nix/store/0gmcz9…-libX11-1.6.1/lib …
``` ```
@ -162,7 +162,7 @@ the `buildInputs = [ ... ]` shown above, not only package names. So the
following is also legal: following is also legal:
```console ```console
$ nix-shell -p sqlite 'git.override { withManual = false; }' $ nix-shell --packages sqlite 'git.override { withManual = false; }'
``` ```
The `-p` flag looks up Nixpkgs in the Nix search path. You can override The `-p` flag looks up Nixpkgs in the Nix search path. You can override
@ -171,7 +171,7 @@ gives you a shell containing the Pan package from a specific revision of
Nixpkgs: Nixpkgs:
```console ```console
$ nix-shell -p pan -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/8a3eea054838b55aca962c3fbde9c83c102b8bf2.tar.gz $ nix-shell --packages pan -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/8a3eea054838b55aca962c3fbde9c83c102b8bf2.tar.gz
[nix-shell:~]$ pan --version [nix-shell:~]$ pan --version
Pan 0.139 Pan 0.139
@ -185,7 +185,7 @@ done by starting the script with the following lines:
```bash ```bash
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
#! nix-shell -i real-interpreter -p packages #! nix-shell -i real-interpreter --packages packages
``` ```
where *real-interpreter* is the “real” script interpreter that will be where *real-interpreter* is the “real” script interpreter that will be
@ -202,7 +202,7 @@ For example, here is a Python script that depends on Python and the
```python ```python
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
#! nix-shell -i python -p python pythonPackages.prettytable #! nix-shell -i python --packages python pythonPackages.prettytable
import prettytable import prettytable
@ -217,7 +217,7 @@ requires Perl and the `HTML::TokeParser::Simple` and `LWP` packages:
```perl ```perl
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
#! nix-shell -i perl -p perl perlPackages.HTMLTokeParserSimple perlPackages.LWP #! nix-shell -i perl --packages perl perlPackages.HTMLTokeParserSimple perlPackages.LWP
use HTML::TokeParser::Simple; use HTML::TokeParser::Simple;
@ -235,7 +235,7 @@ package like Terraform:
```bash ```bash
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
#! nix-shell -i bash -p "terraform.withPlugins (plugins: [ plugins.openstack ])" #! nix-shell -i bash --packages "terraform.withPlugins (plugins: [ plugins.openstack ])"
terraform apply terraform apply
``` ```
@ -251,7 +251,7 @@ branch):
```haskell ```haskell
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
#! nix-shell -i runghc -p "haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])" #! nix-shell -i runghc --packages "haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])"
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-20.03.tar.gz #! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-20.03.tar.gz
import Network.Curl.Download import Network.Curl.Download

View file

@ -23,7 +23,7 @@ produce the same NAR archive. For instance, directory entries are
always sorted so that the actual on-disk order doesnt influence the always sorted so that the actual on-disk order doesnt influence the
result. This means that the cryptographic hash of a NAR dump of a result. This means that the cryptographic hash of a NAR dump of a
path is usable as a fingerprint of the contents of the path. Indeed, path is usable as a fingerprint of the contents of the path. Indeed,
the hashes of store paths stored in Nixs database (see `nix-store -q the hashes of store paths stored in Nixs database (see `nix-store --query
--hash`) are SHA-256 hashes of the NAR dump of each store path. --hash`) are SHA-256 hashes of the NAR dump of each store path.
NAR archives support filenames of unlimited length and 64-bit file NAR archives support filenames of unlimited length and 64-bit file

View file

@ -31,7 +31,7 @@ To copy a whole closure, do something
like: like:
```console ```console
$ nix-store --export $(nix-store -qR paths) > out $ nix-store --export $(nix-store --query --requisites paths) > out
``` ```
To import the whole closure again, run: To import the whole closure again, run:

View file

@ -11,7 +11,7 @@ The following options are allowed for all `nix-store` operations, but may not al
be created in `/nix/var/nix/gcroots/auto/`. For instance, be created in `/nix/var/nix/gcroots/auto/`. For instance,
```console ```console
$ nix-store --add-root /home/eelco/bla/result -r ... $ nix-store --add-root /home/eelco/bla/result --realise ...
$ ls -l /nix/var/nix/gcroots/auto $ ls -l /nix/var/nix/gcroots/auto
lrwxrwxrwx 1 ... 2005-03-13 21:10 dn54lcypm8f8... -> /home/eelco/bla/result lrwxrwxrwx 1 ... 2005-03-13 21:10 dn54lcypm8f8... -> /home/eelco/bla/result

View file

@ -145,7 +145,7 @@ Print the closure (runtime dependencies) of the `svn` program in the
current user environment: current user environment:
```console ```console
$ nix-store -qR $(which svn) $ nix-store --query --requisites $(which svn)
/nix/store/5mbglq5ldqld8sj57273aljwkfvj22mc-subversion-1.1.4 /nix/store/5mbglq5ldqld8sj57273aljwkfvj22mc-subversion-1.1.4
/nix/store/9lz9yc6zgmc0vlqmn2ipcpkjlmbi51vv-glibc-2.3.4 /nix/store/9lz9yc6zgmc0vlqmn2ipcpkjlmbi51vv-glibc-2.3.4
... ...
@ -154,7 +154,7 @@ $ nix-store -qR $(which svn)
Print the build-time dependencies of `svn`: Print the build-time dependencies of `svn`:
```console ```console
$ nix-store -qR $(nix-store -qd $(which svn)) $ nix-store --query --requisites $(nix-store --query --deriver $(which svn))
/nix/store/02iizgn86m42q905rddvg4ja975bk2i4-grep-2.5.1.tar.bz2.drv /nix/store/02iizgn86m42q905rddvg4ja975bk2i4-grep-2.5.1.tar.bz2.drv
/nix/store/07a2bzxmzwz5hp58nf03pahrv2ygwgs3-gcc-wrapper.sh /nix/store/07a2bzxmzwz5hp58nf03pahrv2ygwgs3-gcc-wrapper.sh
/nix/store/0ma7c9wsbaxahwwl04gbw3fcd806ski4-glibc-2.3.4.drv /nix/store/0ma7c9wsbaxahwwl04gbw3fcd806ski4-glibc-2.3.4.drv
@ -168,7 +168,7 @@ the derivation (`-qd`), not the closure of the output path that contains
Show the build-time dependencies as a tree: Show the build-time dependencies as a tree:
```console ```console
$ nix-store -q --tree $(nix-store -qd $(which svn)) $ nix-store --query --tree $(nix-store --query --deriver $(which svn))
/nix/store/7i5082kfb6yjbqdbiwdhhza0am2xvh6c-subversion-1.1.4.drv /nix/store/7i5082kfb6yjbqdbiwdhhza0am2xvh6c-subversion-1.1.4.drv
+---/nix/store/d8afh10z72n8l1cr5w42366abiblgn54-builder.sh +---/nix/store/d8afh10z72n8l1cr5w42366abiblgn54-builder.sh
+---/nix/store/fmzxmpjx2lh849ph0l36snfj9zdibw67-bash-3.0.drv +---/nix/store/fmzxmpjx2lh849ph0l36snfj9zdibw67-bash-3.0.drv
@ -180,7 +180,7 @@ $ nix-store -q --tree $(nix-store -qd $(which svn))
Show all paths that depend on the same OpenSSL library as `svn`: Show all paths that depend on the same OpenSSL library as `svn`:
```console ```console
$ nix-store -q --referrers $(nix-store -q --binding openssl $(nix-store -qd $(which svn))) $ nix-store --query --referrers $(nix-store --query --binding openssl $(nix-store --query --deriver $(which svn)))
/nix/store/23ny9l9wixx21632y2wi4p585qhva1q8-sylpheed-1.0.0 /nix/store/23ny9l9wixx21632y2wi4p585qhva1q8-sylpheed-1.0.0
/nix/store/5mbglq5ldqld8sj57273aljwkfvj22mc-subversion-1.1.4 /nix/store/5mbglq5ldqld8sj57273aljwkfvj22mc-subversion-1.1.4
/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3 /nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3
@ -191,7 +191,7 @@ Show all paths that directly or indirectly depend on the Glibc (C
library) used by `svn`: library) used by `svn`:
```console ```console
$ nix-store -q --referrers-closure $(ldd $(which svn) | grep /libc.so | awk '{print $3}') $ nix-store --query --referrers-closure $(ldd $(which svn) | grep /libc.so | awk '{print $3}')
/nix/store/034a6h4vpz9kds5r6kzb9lhh81mscw43-libgnomeprintui-2.8.2 /nix/store/034a6h4vpz9kds5r6kzb9lhh81mscw43-libgnomeprintui-2.8.2
/nix/store/15l3yi0d45prm7a82pcrknxdh6nzmxza-gawk-3.1.4 /nix/store/15l3yi0d45prm7a82pcrknxdh6nzmxza-gawk-3.1.4
... ...
@ -204,7 +204,7 @@ Make a picture of the runtime dependency graph of the current user
environment: environment:
```console ```console
$ nix-store -q --graph ~/.nix-profile | dot -Tps > graph.ps $ nix-store --query --graph ~/.nix-profile | dot -Tps > graph.ps
$ gv graph.ps $ gv graph.ps
``` ```
@ -212,7 +212,7 @@ Show every garbage collector root that points to a store path that
depends on `svn`: depends on `svn`:
```console ```console
$ nix-store -q --roots $(which svn) $ nix-store --query --roots $(which svn)
/nix/var/nix/profiles/default-81-link /nix/var/nix/profiles/default-81-link
/nix/var/nix/profiles/default-82-link /nix/var/nix/profiles/default-82-link
/home/eelco/.local/state/nix/profiles/profile-97-link /home/eelco/.local/state/nix/profiles/profile-97-link

View file

@ -27,7 +27,7 @@ substitute, then the log is unavailable.
# Example # Example
```console ```console
$ nix-store -l $(which ktorrent) $ nix-store --read-log $(which ktorrent)
building /nix/store/dhc73pvzpnzxhdgpimsd9sw39di66ph1-ktorrent-2.2.1 building /nix/store/dhc73pvzpnzxhdgpimsd9sw39di66ph1-ktorrent-2.2.1
unpacking sources unpacking sources
unpacking source archive /nix/store/p8n1jpqs27mgkjw07pb5269717nzf5f8-ktorrent-2.2.1.tar.gz unpacking source archive /nix/store/p8n1jpqs27mgkjw07pb5269717nzf5f8-ktorrent-2.2.1.tar.gz

View file

@ -99,7 +99,7 @@ This operation is typically used to build [store derivation]s produced by
[store derivation]: @docroot@/glossary.md#gloss-store-derivation [store derivation]: @docroot@/glossary.md#gloss-store-derivation
```console ```console
$ nix-store -r $(nix-instantiate ./test.nix) $ nix-store --realise $(nix-instantiate ./test.nix)
/nix/store/31axcgrlbfsxzmfff1gyj1bf62hvkby2-aterm-2.3.1 /nix/store/31axcgrlbfsxzmfff1gyj1bf62hvkby2-aterm-2.3.1
``` ```
@ -108,7 +108,7 @@ This is essentially what [`nix-build`](@docroot@/command-ref/nix-build.md) does.
To test whether a previously-built derivation is deterministic: To test whether a previously-built derivation is deterministic:
```console ```console
$ nix-build '<nixpkgs>' -A hello --check -K $ nix-build '<nixpkgs>' --attr hello --check -K
``` ```
Use [`nix-store --read-log`](./read-log.md) to show the stderr and stdout of a build: Use [`nix-store --read-log`](./read-log.md) to show the stderr and stdout of a build:

View file

@ -24,6 +24,6 @@ path has changed, and 1 otherwise.
To verify the integrity of the `svn` command and all its dependencies: To verify the integrity of the `svn` command and all its dependencies:
```console ```console
$ nix-store --verify-path $(nix-store -qR $(which svn)) $ nix-store --verify-path $(nix-store --query --requisites $(which svn))
``` ```

View file

@ -162,11 +162,11 @@ Most Nix commands accept the following command-line options:
}: ... }: ...
``` ```
So if you call this Nix expression (e.g., when you do `nix-env -iA So if you call this Nix expression (e.g., when you do `nix-env --install --attr
pkgname`), the function will be called automatically using the pkgname`), the function will be called automatically using the
value [`builtins.currentSystem`](@docroot@/language/builtins.md) for value [`builtins.currentSystem`](@docroot@/language/builtins.md) for
the `system` argument. You can override this using `--arg`, e.g., the `system` argument. You can override this using `--arg`, e.g.,
`nix-env -iA pkgname --arg system \"i686-freebsd\"`. (Note that `nix-env --install --attr pkgname --arg system \"i686-freebsd\"`. (Note that
since the argument is a Nix string literal, you have to escape the since the argument is a Nix string literal, you have to escape the
quotes.) quotes.)
@ -199,7 +199,7 @@ Most Nix commands accept the following command-line options:
For `nix-shell`, this option is commonly used to give you a shell in For `nix-shell`, this option is commonly used to give you a shell in
which you can build the packages returned by the expression. If you which you can build the packages returned by the expression. If you
want to get a shell which contain the *built* packages ready for want to get a shell which contain the *built* packages ready for
use, give your expression to the `nix-shell -p` convenience flag use, give your expression to the `nix-shell --packages ` convenience flag
instead. instead.
- <span id="opt-I">[`-I`](#opt-I)</span> *path*\ - <span id="opt-I">[`-I`](#opt-I)</span> *path*\

View file

@ -77,7 +77,7 @@ $ nix-shell
To get a shell with one of the other [supported compilation environments](#compilation-environments): To get a shell with one of the other [supported compilation environments](#compilation-environments):
```console ```console
$ nix-shell -A devShells.x86_64-linux.native-clang11StdenvPackages $ nix-shell --attr devShells.x86_64-linux.native-clang11StdenvPackages
``` ```
> **Note** > **Note**
@ -139,7 +139,7 @@ $ nix build .#packages.aarch64-linux.default
for flake-enabled Nix, or for flake-enabled Nix, or
```console ```console
$ nix-build -A packages.aarch64-linux.default $ nix-build --attr packages.aarch64-linux.default
``` ```
for classic Nix. for classic Nix.
@ -166,7 +166,7 @@ $ nix build .#nix-ccacheStdenv
for flake-enabled Nix, or for flake-enabled Nix, or
```console ```console
$ nix-build -A nix-ccacheStdenv $ nix-build --attr nix-ccacheStdenv
``` ```
for classic Nix. for classic Nix.

View file

@ -101,11 +101,8 @@
derivation. derivation.
- [output-addressed store object]{#gloss-output-addressed-store-object}\ - [output-addressed store object]{#gloss-output-addressed-store-object}\
A store object whose store path hashes its content. This A [store object] whose [store path] is determined by its contents.
includes derivations, the outputs of This includes derivations, the outputs of [content-addressed derivations](#gloss-content-addressed-derivation), and the outputs of [fixed-output derivations](#gloss-fixed-output-derivation).
[content-addressed derivations](#gloss-content-addressed-derivation),
and the outputs of
[fixed-output derivations](#gloss-fixed-output-derivation).
- [substitute]{#gloss-substitute}\ - [substitute]{#gloss-substitute}\
A substitute is a command invocation stored in the [Nix database] that A substitute is a command invocation stored in the [Nix database] that
@ -163,7 +160,7 @@
build-time dependencies, while the closure of its output path is build-time dependencies, while the closure of its output path is
equivalent to its runtime dependencies. For correct deployment it equivalent to its runtime dependencies. For correct deployment it
is necessary to deploy whole closures, since otherwise at runtime is necessary to deploy whole closures, since otherwise at runtime
files could be missing. The command `nix-store -qR` prints out files could be missing. The command `nix-store --query --requisites ` prints out
closures of store paths. closures of store paths.
As an example, if the [store object] at path `P` contains a [reference] As an example, if the [store object] at path `P` contains a [reference]

View file

@ -47,12 +47,6 @@ The install script will modify the first writable file from amongst
`NIX_INSTALLER_NO_MODIFY_PROFILE` environment variable before executing `NIX_INSTALLER_NO_MODIFY_PROFILE` environment variable before executing
the install script to disable this behaviour. the install script to disable this behaviour.
You can uninstall Nix simply by running:
```console
$ rm -rf /nix
```
# Multi User Installation # Multi User Installation
The multi-user Nix installation creates system users, and a system The multi-user Nix installation creates system users, and a system
@ -84,155 +78,8 @@ The installer will modify `/etc/bashrc`, and `/etc/zshrc` if they exist.
The installer will first back up these files with a `.backup-before-nix` The installer will first back up these files with a `.backup-before-nix`
extension. The installer will also create `/etc/profile.d/nix.sh`. extension. The installer will also create `/etc/profile.d/nix.sh`.
## Uninstalling
### Linux
If you are on Linux with systemd:
1. Remove the Nix daemon service:
```console
sudo systemctl stop nix-daemon.service
sudo systemctl disable nix-daemon.socket nix-daemon.service
sudo systemctl daemon-reload
```
1. Remove systemd service files:
```console
sudo rm /etc/systemd/system/nix-daemon.service /etc/systemd/system/nix-daemon.socket
```
1. The installer script uses systemd-tmpfiles to create the socket directory.
You may also want to remove the configuration for that:
```console
sudo rm /etc/tmpfiles.d/nix-daemon.conf
```
Remove files created by Nix:
```console
sudo rm -rf /nix /etc/nix /etc/profile/nix.sh ~root/.nix-profile ~root/.nix-defexpr ~root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
```
Remove build users and their group:
```console
for i in $(seq 1 32); do
sudo userdel nixbld$i
done
sudo groupdel nixbld
```
There may also be references to Nix in
- `/etc/profile`
- `/etc/bashrc`
- `/etc/zshrc`
which you may remove.
### macOS
1. Edit `/etc/zshrc`, `/etc/bashrc`, and `/etc/bash.bashrc` to remove the lines sourcing
`nix-daemon.sh`, which should look like this:
```bash
# Nix
if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
. '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
fi
# End Nix
```
If these files haven't been altered since installing Nix you can simply put
the backups back in place:
```console
sudo mv /etc/zshrc.backup-before-nix /etc/zshrc
sudo mv /etc/bashrc.backup-before-nix /etc/bashrc
sudo mv /etc/bash.bashrc.backup-before-nix /etc/bash.bashrc
```
This will stop shells from sourcing the file and bringing everything you
installed using Nix in scope.
2. Stop and remove the Nix daemon services:
```console
sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
sudo launchctl unload /Library/LaunchDaemons/org.nixos.darwin-store.plist
sudo rm /Library/LaunchDaemons/org.nixos.darwin-store.plist
```
This stops the Nix daemon and prevents it from being started next time you
boot the system.
3. Remove the `nixbld` group and the `_nixbuildN` users:
```console
sudo dscl . -delete /Groups/nixbld
for u in $(sudo dscl . -list /Users | grep _nixbld); do sudo dscl . -delete /Users/$u; done
```
This will remove all the build users that no longer serve a purpose.
4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store
volume on `/nix`, which looks like
`UUID=<uuid> /nix apfs rw,noauto,nobrowse,suid,owners` or
`LABEL=Nix\040Store /nix apfs rw,nobrowse`. This will prevent automatic
mounting of the Nix Store volume.
5. Edit `/etc/synthetic.conf` to remove the `nix` line. If this is the only
line in the file you can remove it entirely, `sudo rm /etc/synthetic.conf`.
This will prevent the creation of the empty `/nix` directory to provide a
mountpoint for the Nix Store volume.
6. Remove the files Nix added to your system:
```console
sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
```
This gets rid of any data Nix may have created except for the store which is
removed next.
7. Remove the Nix Store volume:
```console
sudo diskutil apfs deleteVolume /nix
```
This will remove the Nix Store volume and everything that was added to the
store.
If the output indicates that the command couldn't remove the volume, you should
make sure you don't have an _unmounted_ Nix Store volume. Look for a
"Nix Store" volume in the output of the following command:
```console
diskutil list
```
If you _do_ see a "Nix Store" volume, delete it by re-running the diskutil
deleteVolume command, but replace `/nix` with the store volume's `diskXsY`
identifier.
> **Note**
>
> After you complete the steps here, you will still have an empty `/nix`
> directory. This is an expected sign of a successful uninstall. The empty
> `/nix` directory will disappear the next time you reboot.
>
> You do not have to reboot to finish uninstalling Nix. The uninstall is
> complete. macOS (Catalina+) directly controls root directories and its
> read-only root will prevent you from manually deleting the empty `/nix`
> mountpoint.
# macOS Installation # macOS Installation
[]{#sect-macos-installation-change-store-prefix}[]{#sect-macos-installation-encrypted-volume}[]{#sect-macos-installation-symlink}[]{#sect-macos-installation-recommended-notes} []{#sect-macos-installation-change-store-prefix}[]{#sect-macos-installation-encrypted-volume}[]{#sect-macos-installation-symlink}[]{#sect-macos-installation-recommended-notes}
<!-- Note: anchors above to catch permalinks to old explanations --> <!-- Note: anchors above to catch permalinks to old explanations -->
@ -281,19 +128,16 @@ this to run the installer, but it may help if you run into trouble:
# Installing a pinned Nix version from a URL # Installing a pinned Nix version from a URL
NixOS.org hosts version-specific installation URLs for all Nix versions Version-specific installation URLs for all Nix versions
since 1.11.16, at `https://releases.nixos.org/nix/nix-version/install`. since 1.11.16 can be found at [releases.nixos.org](https://releases.nixos.org/?prefix=nix/).
The corresponding SHA-256 hash can be found in the directory for the given version.
These install scripts can be used the same as the main NixOS.org These install scripts can be used the same as usual:
installation script:
```console ```console
$ curl -L https://nixos.org/nix/install | sh $ curl -L https://releases.nixos.org/nix/nix-<version>/install | sh
``` ```
In the same directory of the install script are sha256 sums, and gpg
signature files.
# Installing from a binary tarball # Installing from a binary tarball
You can also download a binary tarball that contains Nix and all its You can also download a binary tarball that contains Nix and all its

View file

@ -0,0 +1,148 @@
# Uninstalling Nix
## Single User
If you have a [single-user installation](./installing-binary.md#single-user-installation) of Nix, uninstall it by running:
```console
$ rm -rf /nix
```
## Multi User
Removing a [multi-user installation](./installing-binary.md#multi-user-installation) of Nix is more involved, and depends on the operating system.
### Linux
If you are on Linux with systemd:
1. Remove the Nix daemon service:
```console
sudo systemctl stop nix-daemon.service
sudo systemctl disable nix-daemon.socket nix-daemon.service
sudo systemctl daemon-reload
```
Remove files created by Nix:
```console
sudo rm -rf /etc/nix /etc/profile.d/nix.sh /etc/tmpfiles.d/nix-daemon.conf /nix ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile
```
Remove build users and their group:
```console
for i in $(seq 1 32); do
sudo userdel nixbld$i
done
sudo groupdel nixbld
```
There may also be references to Nix in
- `/etc/bash.bashrc`
- `/etc/bashrc`
- `/etc/profile`
- `/etc/zsh/zshrc`
- `/etc/zshrc`
which you may remove.
### macOS
1. Edit `/etc/zshrc`, `/etc/bashrc`, and `/etc/bash.bashrc` to remove the lines sourcing `nix-daemon.sh`, which should look like this:
```bash
# Nix
if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
. '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
fi
# End Nix
```
If these files haven't been altered since installing Nix you can simply put
the backups back in place:
```console
sudo mv /etc/zshrc.backup-before-nix /etc/zshrc
sudo mv /etc/bashrc.backup-before-nix /etc/bashrc
sudo mv /etc/bash.bashrc.backup-before-nix /etc/bash.bashrc
```
This will stop shells from sourcing the file and bringing everything you
installed using Nix in scope.
2. Stop and remove the Nix daemon services:
```console
sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
sudo launchctl unload /Library/LaunchDaemons/org.nixos.darwin-store.plist
sudo rm /Library/LaunchDaemons/org.nixos.darwin-store.plist
```
This stops the Nix daemon and prevents it from being started next time you
boot the system.
3. Remove the `nixbld` group and the `_nixbuildN` users:
```console
sudo dscl . -delete /Groups/nixbld
for u in $(sudo dscl . -list /Users | grep _nixbld); do sudo dscl . -delete /Users/$u; done
```
This will remove all the build users that no longer serve a purpose.
4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store
volume on `/nix`, which looks like
`UUID=<uuid> /nix apfs rw,noauto,nobrowse,suid,owners` or
`LABEL=Nix\040Store /nix apfs rw,nobrowse`. This will prevent automatic
mounting of the Nix Store volume.
5. Edit `/etc/synthetic.conf` to remove the `nix` line. If this is the only
line in the file you can remove it entirely, `sudo rm /etc/synthetic.conf`.
This will prevent the creation of the empty `/nix` directory to provide a
mountpoint for the Nix Store volume.
6. Remove the files Nix added to your system:
```console
sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
```
This gets rid of any data Nix may have created except for the store which is
removed next.
7. Remove the Nix Store volume:
```console
sudo diskutil apfs deleteVolume /nix
```
This will remove the Nix Store volume and everything that was added to the
store.
If the output indicates that the command couldn't remove the volume, you should
make sure you don't have an _unmounted_ Nix Store volume. Look for a
"Nix Store" volume in the output of the following command:
```console
diskutil list
```
If you _do_ see a "Nix Store" volume, delete it by re-running the diskutil
deleteVolume command, but replace `/nix` with the store volume's `diskXsY`
identifier.
> **Note**
>
> After you complete the steps here, you will still have an empty `/nix`
> directory. This is an expected sign of a successful uninstall. The empty
> `/nix` directory will disappear the next time you reboot.
>
> You do not have to reboot to finish uninstalling Nix. The uninstall is
> complete. macOS (Catalina+) directly controls root directories and its
> read-only root will prevent you from manually deleting the empty `/nix`
> mountpoint.

View file

@ -2,13 +2,13 @@
Multi-user Nix users on macOS can upgrade Nix by running: `sudo -i sh -c Multi-user Nix users on macOS can upgrade Nix by running: `sudo -i sh -c
'nix-channel --update && 'nix-channel --update &&
nix-env -iA nixpkgs.nix && nix-env --install --attr nixpkgs.nix &&
launchctl remove org.nixos.nix-daemon && launchctl remove org.nixos.nix-daemon &&
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist'` launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist'`
Single-user installations of Nix should run this: `nix-channel --update; Single-user installations of Nix should run this: `nix-channel --update;
nix-env -iA nixpkgs.nix nixpkgs.cacert` nix-env --install --attr nixpkgs.nix nixpkgs.cacert`
Multi-user Nix users on Linux should run this with sudo: `nix-channel Multi-user Nix users on Linux should run this with sudo: `nix-channel
--update; nix-env -iA nixpkgs.nix nixpkgs.cacert; systemctl --update; nix-env --install --attr nixpkgs.nix nixpkgs.cacert; systemctl
daemon-reload; systemctl restart nix-daemon` daemon-reload; systemctl restart nix-daemon`

View file

@ -76,7 +76,7 @@ there after an upgrade. This means that you can _roll back_ to the
old version: old version:
```console ```console
$ nix-env --upgrade -A nixpkgs.some-package $ nix-env --upgrade --attr nixpkgs.some-package
$ nix-env --rollback $ nix-env --rollback
``` ```
@ -122,7 +122,7 @@ Nix expressions generally describe how to build a package from
source, so an installation action like source, so an installation action like
```console ```console
$ nix-env --install -A nixpkgs.firefox $ nix-env --install --attr nixpkgs.firefox
``` ```
_could_ cause quite a bit of build activity, as not only Firefox but _could_ cause quite a bit of build activity, as not only Firefox but
@ -158,7 +158,7 @@ Pan newsreader, as described by [its
Nix expression](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/networking/newsreaders/pan/default.nix): Nix expression](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/networking/newsreaders/pan/default.nix):
```console ```console
$ nix-shell '<nixpkgs>' -A pan $ nix-shell '<nixpkgs>' --attr pan
``` ```
Youre then dropped into a shell where you can edit, build and test Youre then dropped into a shell where you can edit, build and test

View file

@ -1,20 +1,43 @@
# Built-in Constants # Built-in Constants
Here are the constants built into the Nix expression evaluator: These constants are built into the Nix language evaluator:
- `builtins`\ - [`builtins`]{#builtins-builtins} (attribute set)
The set `builtins` contains all the built-in functions and values.
You can use `builtins` to test for the availability of features in
the Nix installation, e.g.,
```nix
if builtins ? getEnv then builtins.getEnv "PATH" else ""
```
This allows a Nix expression to fall back gracefully on older Nix
installations that dont have the desired built-in function.
- [`builtins.currentSystem`]{#builtins-currentSystem}\ Contains all the [built-in functions](./builtins.md) and values, in order to avoid polluting the global scope.
The built-in value `currentSystem` evaluates to the Nix platform
identifier for the Nix installation on which the expression is being Since built-in functions were added over time, [testing for attributes](./operators.md#has-attribute) in `builtins` can be used for graceful fallback on older Nix installations:
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.
```nix
if builtins ? getEnv then builtins.getEnv "PATH" else ""
```
- [`builtins.currentSystem`]{#builtins-currentSystem} (string)
The built-in value `currentSystem` evaluates to the Nix platform
identifier for the Nix installation on which the expression is being
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
- [`builtins.currentTime`]{#builtins-currentTime} (integer)
Return the [Unix time](https://en.wikipedia.org/wiki/Unix_time) at first evaluation.
Repeated references to that name will re-use the initially obtained value.
Example:
```console
$ nix repl
Welcome to Nix 2.15.1 Type :? for help.
nix-repl> builtins.currentTime
1683705525
nix-repl> builtins.currentTime
1683705525
```
The [store path](@docroot@/glossary.md#gloss-store-path) of a derivation depending on `currentTime` will differ for each evaluation.
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).

View file

@ -1,16 +1,16 @@
# Built-in Functions # Built-in Functions
This section lists the functions built into the Nix expression This section lists the functions built into the Nix language evaluator.
evaluator. (The built-in function `derivation` is discussed above.) All built-in functions are available through the global [`builtins`](./builtin-constants.md#builtins-builtins) constant.
Some built-ins, such as `derivation`, are always in scope of every Nix
expression; you can just access them right away. But to prevent For convenience, some built-ins are can be accessed directly:
polluting the namespace too much, most built-ins are not in
scope. Instead, you can access them through the `builtins` built-in - [`derivation`](#builtins-derivation)
value, which is a set that contains all built-in functions and values. - [`import`](#builtins-import)
For instance, `derivation` is also available as `builtins.derivation`. - [`abort`](#builtins-abort)
- [`throw`](#builtins-throw)
<dl> <dl>
<dt><code>derivation <var>attrs</var></code>; <dt id="builtins-derivation"><a href="#builtins-derivation"><code>derivation <var>attrs</var></code></a></dt>
<code>builtins.derivation <var>attrs</var></code></dt>
<dd><p><var>derivation</var> is described in <dd><p><var>derivation</var> is described in
<a href="derivations.md">its own section</a>.</p></dd> <a href="derivations.md">its own section</a>.</p></dd>

View file

@ -2,8 +2,11 @@
## Recursive sets ## Recursive sets
Recursive sets are just normal sets, but the attributes can refer to Recursive sets are like normal [attribute sets](./values.md#attribute-set), but the attributes can refer to each other.
each other. For example,
> *rec-attrset* = `rec {` [ *name* `=` *expr* `;` `]`... `}`
Example:
```nix ```nix
rec { rec {
@ -12,7 +15,9 @@ rec {
}.x }.x
``` ```
evaluates to `123`. Note that without `rec` the binding `x = y;` would This evaluates to `123`.
Note that without `rec` the binding `x = y;` would
refer to the variable `y` in the surrounding scope, if one exists, and refer to the variable `y` in the surrounding scope, if one exists, and
would be invalid if no such variable exists. That is, in a normal would be invalid if no such variable exists. That is, in a normal
(non-recursive) set, attributes are not added to the lexical scope; in a (non-recursive) set, attributes are not added to the lexical scope; in a
@ -33,7 +38,10 @@ will crash with an `infinite recursion encountered` error message.
## Let-expressions ## Let-expressions
A let-expression allows you to define local variables for an expression. A let-expression allows you to define local variables for an expression.
For instance,
> *let-in* = `let` [ *identifier* = *expr* ]... `in` *expr*
Example:
```nix ```nix
let let
@ -42,18 +50,19 @@ let
in x + y in x + y
``` ```
evaluates to `"foobar"`. This evaluates to `"foobar"`.
## Inheriting attributes ## Inheriting attributes
When defining a set or in a let-expression it is often convenient to When defining an [attribute set](./values.md#attribute-set) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes).
copy variables from the surrounding lexical scope (e.g., when you want This can be shortened using the `inherit` keyword.
to propagate attributes). This can be shortened using the `inherit`
keyword. For instance, Example:
```nix ```nix
let x = 123; in let x = 123; in
{ inherit x; {
inherit x;
y = 456; y = 456;
} }
``` ```
@ -62,15 +71,23 @@ is equivalent to
```nix ```nix
let x = 123; in let x = 123; in
{ x = x; {
x = x;
y = 456; y = 456;
} }
``` ```
and both evaluate to `{ x = 123; y = 456; }`. (Note that this works and both evaluate to `{ x = 123; y = 456; }`.
because `x` is added to the lexical scope by the `let` construct.) It is
also possible to inherit attributes from another set. For instance, in > **Note**
this fragment from `all-packages.nix`, >
> This works because `x` is added to the lexical scope by the `let` construct.
It is also possible to inherit attributes from another attribute set.
Example:
In this fragment from `all-packages.nix`,
```nix ```nix
graphviz = (import ../tools/graphics/graphviz) { graphviz = (import ../tools/graphics/graphviz) {

View file

@ -35,17 +35,14 @@
## Attribute selection ## Attribute selection
> *attrset* `.` *attrpath* \[ `or` *expr* \]
Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*. Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*.
If the attribute doesnt exist, return *value* if provided, otherwise abort evaluation. If the attribute doesnt exist, return the *expr* after `or` if provided, otherwise abort evaluation.
<!-- FIXME: the following should to into its own language syntax section, but that needs more work to fit in well --> An attribute path is a dot-separated list of [attribute names](./values.md#attribute-set).
An attribute path is a dot-separated list of attribute names. > *attrpath* = *name* [ `.` *name* ]...
An attribute name can be an identifier or a string.
> *attrpath* = *name* [ `.` *name* ]... \
> *name* = *identifier* | *string* \
> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*`
[Attribute selection]: #attribute-selection [Attribute selection]: #attribute-selection

View file

@ -164,9 +164,17 @@ Note that lists are only lazy in values, and they are strict in length.
An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`).
An attribute name can be an identifier or a [string](#string).
An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`).
> *name* = *identifier* | *string* \
> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*`
Names and values are separated by an equal sign (`=`). Names and values are separated by an equal sign (`=`).
Each value is an arbitrary expression terminated by a semicolon (`;`). Each value is an arbitrary expression terminated by a semicolon (`;`).
> *attrset* = `{` [ *name* `=` *expr* `;` `]`... `}`
Attributes can appear in any order. Attributes can appear in any order.
An attribute name may only occur once. An attribute name may only occur once.
@ -182,21 +190,29 @@ Example:
This defines a set with attributes named `x`, `text`, `y`. This defines a set with attributes named `x`, `text`, `y`.
Attributes can be selected from a set using the `.` operator. For Attributes can be accessed with the [`.` operator](./operators.md#attribute-selection).
instance,
Example:
```nix ```nix
{ a = "Foo"; b = "Bar"; }.a { a = "Foo"; b = "Bar"; }.a
``` ```
evaluates to `"Foo"`. It is possible to provide a default value in an This evaluates to `"Foo"`.
attribute selection using the `or` keyword. For example,
It is possible to provide a default value in an attribute selection using the `or` keyword.
Example:
```nix ```nix
{ a = "Foo"; b = "Bar"; }.c or "Xyzzy" { a = "Foo"; b = "Bar"; }.c or "Xyzzy"
``` ```
will evaluate to `"Xyzzy"` because there is no `c` attribute in the set. ```nix
{ a = "Foo"; b = "Bar"; }.c.d.e.f.g or "Xyzzy"
```
will both evaluate to `"Xyzzy"` because there is no `c` attribute in the set.
You can use arbitrary double-quoted strings as attribute names: You can use arbitrary double-quoted strings as attribute names:

View file

@ -47,7 +47,7 @@ $ nix-channel --update
You can view the set of available packages in Nixpkgs: You can view the set of available packages in Nixpkgs:
```console ```console
$ nix-env -qaP $ nix-env --query --available --attr-path
nixpkgs.aterm aterm-2.2 nixpkgs.aterm aterm-2.2
nixpkgs.bash bash-3.0 nixpkgs.bash bash-3.0
nixpkgs.binutils binutils-2.15 nixpkgs.binutils binutils-2.15
@ -65,7 +65,7 @@ If you downloaded Nixpkgs yourself, or if you checked it out from GitHub,
then you need to pass the path to your Nixpkgs tree using the `-f` flag: then you need to pass the path to your Nixpkgs tree using the `-f` flag:
```console ```console
$ nix-env -qaPf /path/to/nixpkgs $ nix-env --query --available --attr-path --file /path/to/nixpkgs
aterm aterm-2.2 aterm aterm-2.2
bash bash-3.0 bash bash-3.0
@ -77,7 +77,7 @@ Nixpkgs.
You can filter the packages by name: You can filter the packages by name:
```console ```console
$ nix-env -qaP firefox $ nix-env --query --available --attr-path firefox
nixpkgs.firefox-esr firefox-91.3.0esr nixpkgs.firefox-esr firefox-91.3.0esr
nixpkgs.firefox firefox-94.0.1 nixpkgs.firefox firefox-94.0.1
``` ```
@ -85,7 +85,7 @@ nixpkgs.firefox firefox-94.0.1
and using regular expressions: and using regular expressions:
```console ```console
$ nix-env -qaP 'firefox.*' $ nix-env --query --available --attr-path 'firefox.*'
``` ```
It is also possible to see the *status* of available packages, i.e., It is also possible to see the *status* of available packages, i.e.,
@ -93,7 +93,7 @@ whether they are installed into the user environment and/or present in
the system: the system:
```console ```console
$ nix-env -qaPs $ nix-env --query --available --attr-path --status
-PS nixpkgs.bash bash-3.0 -PS nixpkgs.bash bash-3.0
--S nixpkgs.binutils binutils-2.15 --S nixpkgs.binutils binutils-2.15
@ -110,10 +110,10 @@ which is Nixs mechanism for doing binary deployment. It just means that
Nix knows that it can fetch a pre-built package from somewhere Nix knows that it can fetch a pre-built package from somewhere
(typically a network server) instead of building it locally. (typically a network server) instead of building it locally.
You can install a package using `nix-env -iA`. For instance, You can install a package using `nix-env --install --attr `. For instance,
```console ```console
$ nix-env -iA nixpkgs.subversion $ nix-env --install --attr nixpkgs.subversion
``` ```
will install the package called `subversion` from `nixpkgs` channel (which is, of course, the will install the package called `subversion` from `nixpkgs` channel (which is, of course, the
@ -143,14 +143,14 @@ instead of the attribute path, as `nix-env` does not record which attribute
was used for installing: was used for installing:
```console ```console
$ nix-env -e subversion $ nix-env --uninstall subversion
``` ```
Upgrading to a new version is just as easy. If you have a new release of Upgrading to a new version is just as easy. If you have a new release of
Nix Packages, you can do: Nix Packages, you can do:
```console ```console
$ nix-env -uA nixpkgs.subversion $ nix-env --upgrade --attr nixpkgs.subversion
``` ```
This will *only* upgrade Subversion if there is a “newer” version in the This will *only* upgrade Subversion if there is a “newer” version in the
@ -163,15 +163,15 @@ whatever version is in the Nix expressions, use `-i` instead of `-u`;
You can also upgrade all packages for which there are newer versions: You can also upgrade all packages for which there are newer versions:
```console ```console
$ nix-env -u $ nix-env --upgrade
``` ```
Sometimes its useful to be able to ask what `nix-env` would do, without Sometimes its useful to be able to ask what `nix-env` would do, without
actually doing it. For instance, to find out what packages would be actually doing it. For instance, to find out what packages would be
upgraded by `nix-env -u`, you can do upgraded by `nix-env --upgrade `, you can do
```console ```console
$ nix-env -u --dry-run $ nix-env --upgrade --dry-run
(dry run; not doing anything) (dry run; not doing anything)
upgrading `libxslt-1.1.0' to `libxslt-1.1.10' upgrading `libxslt-1.1.0' to `libxslt-1.1.10'
upgrading `graphviz-1.10' to `graphviz-1.12' upgrading `graphviz-1.10' to `graphviz-1.12'

View file

@ -9,7 +9,7 @@ The daemon that handles binary cache requests via HTTP, `nix-serve`, is
not part of the Nix distribution, but you can install it from Nixpkgs: not part of the Nix distribution, but you can install it from Nixpkgs:
```console ```console
$ nix-env -iA nixpkgs.nix-serve $ nix-env --install --attr nixpkgs.nix-serve
``` ```
You can then start the server, listening for HTTP connections on You can then start the server, listening for HTTP connections on
@ -35,7 +35,7 @@ On the client side, you can tell Nix to use your binary cache using
`--substituters`, e.g.: `--substituters`, e.g.:
```console ```console
$ nix-env -iA nixpkgs.firefox --substituters http://avalon:8080/ $ nix-env --install --attr nixpkgs.firefox --substituters http://avalon:8080/
``` ```
The option `substituters` tells Nix to use this binary cache in The option `substituters` tells Nix to use this binary cache in

View file

@ -43,7 +43,7 @@ operations (via the symlink `~/.nix-defexpr/channels`). Consequently,
you can then say you can then say
```console ```console
$ nix-env -u $ nix-env --upgrade
``` ```
to upgrade all packages in your profile to the latest versions available to upgrade all packages in your profile to the latest versions available

View file

@ -15,7 +15,7 @@ With `nix-store
path (that is, the path and all its dependencies) to a file, and then path (that is, the path and all its dependencies) to a file, and then
unpack that file into another Nix store. For example, unpack that file into another Nix store. For example,
$ nix-store --export $(nix-store -qR $(type -p firefox)) > firefox.closure $ nix-store --export $(nix-store --query --requisites $(type -p firefox)) > firefox.closure
writes the closure of Firefox to a file. You can then copy this file to writes the closure of Firefox to a file. You can then copy this file to
another machine and install the closure: another machine and install the closure:
@ -27,7 +27,7 @@ store are ignored. It is also possible to pipe the export into another
command, e.g. to copy and install a closure directly to/on another command, e.g. to copy and install a closure directly to/on another
machine: machine:
$ nix-store --export $(nix-store -qR $(type -p firefox)) | bzip2 | \ $ nix-store --export $(nix-store --query --requisites $(type -p firefox)) | bzip2 | \
ssh alice@itchy.example.org "bunzip2 | nix-store --import" ssh alice@itchy.example.org "bunzip2 | nix-store --import"
However, `nix-copy-closure` is generally more efficient because it only However, `nix-copy-closure` is generally more efficient because it only

View file

@ -39,7 +39,7 @@ just Subversion 1.1.2 (arrows in the figure indicate symlinks). This
would be what we would obtain if we had done would be what we would obtain if we had done
```console ```console
$ nix-env -iA nixpkgs.subversion $ nix-env --install --attr nixpkgs.subversion
``` ```
on a set of Nix expressions that contained Subversion 1.1.2. on a set of Nix expressions that contained Subversion 1.1.2.
@ -54,7 +54,7 @@ environment is generated based on the current one. For instance,
generation 43 was created from generation 42 when we did generation 43 was created from generation 42 when we did
```console ```console
$ nix-env -iA nixpkgs.subversion nixpkgs.firefox $ nix-env --install --attr nixpkgs.subversion nixpkgs.firefox
``` ```
on a set of Nix expressions that contained Firefox and a new version of on a set of Nix expressions that contained Firefox and a new version of
@ -127,7 +127,7 @@ All `nix-env` operations work on the profile pointed to by
(abbreviation `-p`): (abbreviation `-p`):
```console ```console
$ nix-env -p /nix/var/nix/profiles/other-profile -iA nixpkgs.subversion $ nix-env --profile /nix/var/nix/profiles/other-profile --install --attr nixpkgs.subversion
``` ```
This will *not* change the `~/.nix-profile` symlink. This will *not* change the `~/.nix-profile` symlink.

View file

@ -6,7 +6,7 @@ automatically fetching any store paths in Firefoxs closure if they are
available on the server `avalon`: available on the server `avalon`:
```console ```console
$ nix-env -iA nixpkgs.firefox --substituters ssh://alice@avalon $ nix-env --install --attr nixpkgs.firefox --substituters ssh://alice@avalon
``` ```
This works similar to the binary cache substituter that Nix usually This works similar to the binary cache substituter that Nix usually
@ -25,7 +25,7 @@ You can also copy the closure of some store path, without installing it
into your profile, e.g. into your profile, e.g.
```console ```console
$ nix-store -r /nix/store/m85bxg…-firefox-34.0.5 --substituters $ nix-store --realise /nix/store/m85bxg…-firefox-34.0.5 --substituters
ssh://alice@avalon ssh://alice@avalon
``` ```

View file

@ -0,0 +1,8 @@
# Release 2.16 (2023-05-31)
* Speed-up of downloads from binary caches.
The number of parallel downloads (also known as substitutions) has been separated from the [`--max-jobs` setting](../command-ref/conf-file.md#conf-max-jobs).
The new setting is called [`max-substitution-jobs`](../command-ref/conf-file.md#conf-max-substitution-jobs).
The number of parallel downloads is now set to 16 by default (previously, the default was 1 due to the coupling to build jobs).
* The function [`builtins.replaceStrings`](@docroot@/language/builtins.md#builtins-replaceStrings) is now lazy in the value of its second argument `to`. That is, `to` is only evaluated when its corresponding pattern in `from` is matched in the string `s`.

View file

@ -190,6 +190,12 @@ let
cp -a ${rootEnv}/* $out/ cp -a ${rootEnv}/* $out/
ln -s ${manifest} $out/manifest.nix ln -s ${manifest} $out/manifest.nix
''; '';
flake-registry-path = if (flake-registry == null) then
null
else if (builtins.readFileType (toString flake-registry)) == "directory" then
"${flake-registry}/flake-registry.json"
else
flake-registry;
in in
pkgs.runCommand "base-system" pkgs.runCommand "base-system"
{ {
@ -202,7 +208,7 @@ let
]; ];
allowSubstitutes = false; allowSubstitutes = false;
preferLocalBuild = true; preferLocalBuild = true;
} '' } (''
env env
set -x set -x
mkdir -p $out/etc mkdir -p $out/etc
@ -249,15 +255,15 @@ let
ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env
ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh
'' + (lib.optionalString (flake-registry != null) '' '' + (lib.optionalString (flake-registry-path != null) ''
nixCacheDir="/root/.cache/nix" nixCacheDir="/root/.cache/nix"
mkdir -p $out$nixCacheDir mkdir -p $out$nixCacheDir
globalFlakeRegistryPath="$nixCacheDir/flake-registry.json" globalFlakeRegistryPath="$nixCacheDir/flake-registry.json"
ln -s ${flake-registry}/flake-registry.json $out$globalFlakeRegistryPath ln -s ${flake-registry-path} $out$globalFlakeRegistryPath
mkdir -p $out/nix/var/nix/gcroots/auto mkdir -p $out/nix/var/nix/gcroots/auto
rootName=$(${pkgs.nix}/bin/nix --extra-experimental-features nix-command hash file --type sha1 --base32 <(echo -n $globalFlakeRegistryPath)) rootName=$(${pkgs.nix}/bin/nix --extra-experimental-features nix-command hash file --type sha1 --base32 <(echo -n $globalFlakeRegistryPath))
ln -s $globalFlakeRegistryPath $out/nix/var/nix/gcroots/auto/$rootName ln -s $globalFlakeRegistryPath $out/nix/var/nix/gcroots/auto/$rootName
''); ''));
in in
pkgs.dockerTools.buildLayeredImageWithNixDb { pkgs.dockerTools.buildLayeredImageWithNixDb {

View file

@ -42,12 +42,12 @@ The team meets twice a week:
- Discussion meeting: [Fridays 13:00-14:00 CET](https://calendar.google.com/calendar/event?eid=MHNtOGVuNWtrZXNpZHR2bW1sM3QyN2ZjaGNfMjAyMjExMjVUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) - Discussion meeting: [Fridays 13:00-14:00 CET](https://calendar.google.com/calendar/event?eid=MHNtOGVuNWtrZXNpZHR2bW1sM3QyN2ZjaGNfMjAyMjExMjVUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn)
1. Triage issues and pull requests from the _No Status_ column (30 min) 1. Triage issues and pull requests from the [No Status](#no-status) column (30 min)
2. Discuss issues and pull requests from the _To discuss_ column (30 min) 2. Discuss issues and pull requests from the [To discuss](#to-discuss) column (30 min)
- Work meeting: [Mondays 13:00-15:00 CET](https://calendar.google.com/calendar/event?eid=NTM1MG1wNGJnOGpmOTZhYms3bTB1bnY5cWxfMjAyMjExMjFUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) - Work meeting: [Mondays 13:00-15:00 CET](https://calendar.google.com/calendar/event?eid=NTM1MG1wNGJnOGpmOTZhYms3bTB1bnY5cWxfMjAyMjExMjFUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn)
1. Code review on pull requests from _In review_. 1. Code review on pull requests from [In review](#in-review).
2. Other chores and tasks. 2. Other chores and tasks.
Meeting notes are collected on a [collaborative scratchpad](https://pad.lassul.us/Cv7FpYx-Ri-4VjUykQOLAw), and published on Discourse under the [Nix category](https://discourse.nixos.org/c/dev/nix/50). Meeting notes are collected on a [collaborative scratchpad](https://pad.lassul.us/Cv7FpYx-Ri-4VjUykQOLAw), and published on Discourse under the [Nix category](https://discourse.nixos.org/c/dev/nix/50).
@ -58,64 +58,74 @@ The team uses a [GitHub project board](https://github.com/orgs/NixOS/projects/19
Items on the board progress through the following states: Items on the board progress through the following states:
- No Status ### No Status
During the discussion meeting, the team triages new items. During the discussion meeting, the team triages new items.
To be considered, issues and pull requests must have a high-level description to provide the whole team with the necessary context at a glance. To be considered, issues and pull requests must have a high-level description to provide the whole team with the necessary context at a glance.
On every meeting, at least one item from each of the following categories is inspected: On every meeting, at least one item from each of the following categories is inspected:
1. [critical](https://github.com/NixOS/nix/labels/critical) 1. [critical](https://github.com/NixOS/nix/labels/critical)
2. [security](https://github.com/NixOS/nix/labels/security) 2. [security](https://github.com/NixOS/nix/labels/security)
3. [regression](https://github.com/NixOS/nix/labels/regression) 3. [regression](https://github.com/NixOS/nix/labels/regression)
4. [bug](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Abug+sort%3Areactions-%2B1-desc) 4. [bug](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Abug+sort%3Areactions-%2B1-desc)
5. [tests of existing functionality](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Atests+-label%3Afeature+sort%3Areactions-%2B1-desc) 5. [tests of existing functionality](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Atests+-label%3Afeature+sort%3Areactions-%2B1-desc)
- [oldest pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc) - [oldest pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc)
- [most popular pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Areactions-%2B1-desc) - [most popular pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Areactions-%2B1-desc)
- [oldest issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc) - [oldest issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc)
- [most popular issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) - [most popular issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc)
Team members can also add pull requests or issues they would like the whole team to consider. Team members can also add pull requests or issues they would like the whole team to consider.
To ensure process quality and reliability, all non-trivial pull requests must be triaged before merging.
If there is disagreement on the general idea behind an issue or pull request, it is moved to _To discuss_, otherwise to _In review_. If there is disagreement on the general idea behind an issue or pull request, it is moved to [To discuss](#to-discuss).
Otherwise, the issue or pull request in questions get the label [`idea approved`](https://github.com/NixOS/nix/labels/idea%20approved).
For issues this means that an implementation is welcome and will be prioritised for review.
For pull requests this means that:
- Unfinished work is encouraged to be continued.
- A reviewer is assigned to take responsibility for getting the pull request merged.
The item is moved to the [Assigned](#assigned) column.
- If needed, the team can decide to do a collarorative review.
Then the item is moved to the [In review](#in-review) column, and review session is scheduled.
To ensure process quality and reliability, all non-trivial pull requests must be triaged before merging. What constitutes a trivial pull request is up to maintainers' judgement.
What constitutes a trivial pull request is up to maintainers' judgement.
- To discuss ### To discuss
Pull requests and issues that are deemed important and controversial are discussed by the team during discussion meetings. Pull requests and issues that are deemed important and controversial are discussed by the team during discussion meetings.
This may be where the merit of the change itself or the implementation strategy is contested by a team member. This may be where the merit of the change itself or the implementation strategy is contested by a team member.
As a general guideline, the order of items is determined as follows: As a general guideline, the order of items is determined as follows:
- Prioritise pull requests over issues - Prioritise pull requests over issues
Contributors who took the time to implement concrete change proposals should not wait indefinitely. Contributors who took the time to implement concrete change proposals should not wait indefinitely.
- Prioritise fixing bugs and testing over documentation, improvements or new features - Prioritise fixing bugs and testing over documentation, improvements or new features
The team values stability and accessibility higher than raw functionality. The team values stability and accessibility higher than raw functionality.
- Interleave issues and PRs - Interleave issues and PRs
This way issues without attempts at a solution get a chance to get addressed. This way issues without attempts at a solution get a chance to get addressed.
- In review ### In review
Pull requests in this column are reviewed together during work meetings. Pull requests in this column are reviewed together during work meetings.
This is both for spreading implementation knowledge and for establishing common values in code reviews. This is both for spreading implementation knowledge and for establishing common values in code reviews.
When the overall direction is agreed upon, even when further changes are required, the pull request is assigned to one team member. When the overall direction is agreed upon, even when further changes are required, the pull request is assigned to one team member.
- Assigned for merging ### Assigned
One team member is assigned to each of these pull requests. One team member is assigned to each of these pull requests.
They will communicate with the authors, and make the final approval once all remaining issues are addressed. They will communicate with the authors, and make the final approval once all remaining issues are addressed.
If more substantive issues arise, the assignee can move the pull request back to _To discuss_ to involve the team again. If more substantive issues arise, the assignee can move the pull request back to [To discuss](#to-discuss) or [In review](#in-review) to involve the team again.
### Flowchart
The process is illustrated in the following diagram: The process is illustrated in the following diagram:

View file

@ -119,8 +119,7 @@ release:
TODO: This script requires the right AWS credentials. Document. TODO: This script requires the right AWS credentials. Document.
TODO: This script currently requires a TODO: This script currently requires a
`/home/eelco/Dev/nix-pristine` and `/home/eelco/Dev/nix-pristine`.
`/home/eelco/Dev/nixpkgs-pristine`.
TODO: trigger nixos.org netlify: https://docs.netlify.com/configure-builds/build-hooks/ TODO: trigger nixos.org netlify: https://docs.netlify.com/configure-builds/build-hooks/
@ -141,7 +140,7 @@ release:
$ git checkout master $ git checkout master
$ git pull $ git pull
$ NEW_VERSION=2.13.0 $ NEW_VERSION=2.13.0
$ echo -n $NEW_VERSION > .version $ echo $NEW_VERSION > .version
$ git checkout -b bump-$NEW_VERSION $ git checkout -b bump-$NEW_VERSION
$ git commit -a -m 'Bump version' $ git commit -a -m 'Bump version'
$ git push --set-upstream origin bump-$NEW_VERSION $ git push --set-upstream origin bump-$NEW_VERSION

View file

@ -15,7 +15,6 @@ my $evalId = $ARGV[0] or die "Usage: $0 EVAL-ID\n";
my $releasesBucketName = "nix-releases"; my $releasesBucketName = "nix-releases";
my $channelsBucketName = "nix-channels"; my $channelsBucketName = "nix-channels";
my $nixpkgsDir = "/home/eelco/Dev/nixpkgs-pristine";
my $TMPDIR = $ENV{'TMPDIR'} // "/tmp"; my $TMPDIR = $ENV{'TMPDIR'} // "/tmp";
@ -81,6 +80,38 @@ my $s3_us = Net::Amazon::S3->new(
my $channelsBucket = $s3_us->bucket($channelsBucketName) or die; my $channelsBucket = $s3_us->bucket($channelsBucketName) or die;
sub getStorePath {
my ($jobName, $output) = @_;
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
return $buildInfo->{buildoutputs}->{$output or "out"}->{path} or die "cannot get store path for '$jobName'";
}
sub copyManual {
my $manual = getStorePath("build.x86_64-linux", "doc");
print "$manual\n";
my $manualNar = "$tmpDir/$releaseName-manual.nar.xz";
print "$manualNar\n";
unless (-e $manualNar) {
system("NIX_REMOTE=$binaryCache nix store dump-path '$manual' | xz > '$manualNar'.tmp") == 0
or die "unable to fetch $manual\n";
rename("$manualNar.tmp", $manualNar) or die;
}
unless (-e "$tmpDir/manual") {
system("xz -d < '$manualNar' | nix-store --restore $tmpDir/manual.tmp") == 0
or die "unable to unpack $manualNar\n";
rename("$tmpDir/manual.tmp/share/doc/nix/manual", "$tmpDir/manual") or die;
system("rm -rf '$tmpDir/manual.tmp'") == 0 or die;
}
system("aws s3 sync '$tmpDir/manual' s3://$releasesBucketName/$releaseDir/manual") == 0
or die "syncing manual to S3\n";
}
copyManual;
sub downloadFile { sub downloadFile {
my ($jobName, $productNr, $dstName) = @_; my ($jobName, $productNr, $dstName) = @_;
@ -180,9 +211,20 @@ if ($isLatest) {
system("docker manifest push nixos/nix:latest") == 0 or die; system("docker manifest push nixos/nix:latest") == 0 or die;
} }
# Upload nix-fallback-paths.nix.
write_file("$tmpDir/fallback-paths.nix",
"{\n" .
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
" aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
"}\n");
# Upload release files to S3. # Upload release files to S3.
for my $fn (glob "$tmpDir/*") { for my $fn (glob "$tmpDir/*") {
my $name = basename($fn); my $name = basename($fn);
next if $name eq "manual";
my $dstKey = "$releaseDir/" . $name; my $dstKey = "$releaseDir/" . $name;
unless (defined $releasesBucket->head_key($dstKey)) { unless (defined $releasesBucket->head_key($dstKey)) {
print STDERR "uploading $fn to s3://$releasesBucketName/$dstKey...\n"; print STDERR "uploading $fn to s3://$releasesBucketName/$dstKey...\n";
@ -190,8 +232,7 @@ for my $fn (glob "$tmpDir/*") {
my $configuration = (); my $configuration = ();
$configuration->{content_type} = "application/octet-stream"; $configuration->{content_type} = "application/octet-stream";
if ($fn =~ /.sha256|install/) { if ($fn =~ /.sha256|install|\.nix$/) {
# Text files
$configuration->{content_type} = "text/plain"; $configuration->{content_type} = "text/plain";
} }
@ -200,28 +241,6 @@ for my $fn (glob "$tmpDir/*") {
} }
} }
# Update nix-fallback-paths.nix.
if ($isLatest) {
system("cd $nixpkgsDir && git pull") == 0 or die;
sub getStorePath {
my ($jobName) = @_;
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
return $buildInfo->{buildoutputs}->{out}->{path} or die "cannot get store path for '$jobName'";
}
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
"{\n" .
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
" aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
"}\n");
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
}
# Update the "latest" symlink. # Update the "latest" symlink.
$channelsBucket->add_key( $channelsBucket->add_key(
"nix-latest/install", "", "nix-latest/install", "",

5
mk/cxx-big-literal.mk Normal file
View file

@ -0,0 +1,5 @@
%.gen.hh: %
@echo 'R"foo(' >> $@.tmp
$(trace-gen) cat $< >> $@.tmp
@echo ')foo"' >> $@.tmp
@mv $@.tmp $@

View file

@ -101,6 +101,7 @@ include mk/libraries.mk
include mk/programs.mk include mk/programs.mk
include mk/patterns.mk include mk/patterns.mk
include mk/templates.mk include mk/templates.mk
include mk/cxx-big-literal.mk
include mk/tests.mk include mk/tests.mk

View file

@ -246,8 +246,15 @@ printf -v _OLD_LINE_FMT "%b" $'\033[1;7;31m-'"$ESC ${RED}%L${ESC}"
printf -v _NEW_LINE_FMT "%b" $'\033[1;7;32m+'"$ESC ${GREEN}%L${ESC}" printf -v _NEW_LINE_FMT "%b" $'\033[1;7;32m+'"$ESC ${GREEN}%L${ESC}"
_diff() { _diff() {
# macOS Ventura doesn't ship with GNU diff. Print similar output except
# without +/- markers or dimming
if diff --version | grep -q "Apple diff"; then
printf -v CHANGED_GROUP_FORMAT "%b" "${GREEN}%>${RED}%<${ESC}"
diff --changed-group-format="$CHANGED_GROUP_FORMAT" "$@"
else
# simple colorized diff comatible w/ pre `--color` versions # simple colorized diff comatible w/ pre `--color` versions
diff --unchanged-group-format="$_UNCHANGED_GRP_FMT" --old-line-format="$_OLD_LINE_FMT" --new-line-format="$_NEW_LINE_FMT" --unchanged-line-format=" %L" "$@" diff --unchanged-group-format="$_UNCHANGED_GRP_FMT" --old-line-format="$_OLD_LINE_FMT" --new-line-format="$_NEW_LINE_FMT" --unchanged-line-format=" %L" "$@"
fi
} }
confirm_rm() { confirm_rm() {
@ -873,7 +880,7 @@ configure_shell_profile() {
fi fi
done done
task "Setting up shell profiles for Fish with with ${PROFILE_FISH_SUFFIX} inside ${PROFILE_FISH_PREFIXES[*]}" task "Setting up shell profiles for Fish with ${PROFILE_FISH_SUFFIX} inside ${PROFILE_FISH_PREFIXES[*]}"
for fish_prefix in "${PROFILE_FISH_PREFIXES[@]}"; do for fish_prefix in "${PROFILE_FISH_PREFIXES[@]}"; do
if [ ! -d "$fish_prefix" ]; then if [ ! -d "$fish_prefix" ]; then
# this specific prefix (ie: /etc/fish) is very likely to exist # this specific prefix (ie: /etc/fish) is very likely to exist

View file

@ -92,7 +92,7 @@ poly_configure_nix_daemon_service() {
task "Setting up the nix-daemon systemd service" task "Setting up the nix-daemon systemd service"
_sudo "to create the nix-daemon tmpfiles config" \ _sudo "to create the nix-daemon tmpfiles config" \
ln -sfn /nix/var/nix/profiles/default/$TMPFILES_SRC $TMPFILES_DEST ln -sfn "/nix/var/nix/profiles/default$TMPFILES_SRC" "$TMPFILES_DEST"
_sudo "to run systemd-tmpfiles once to pick that path up" \ _sudo "to run systemd-tmpfiles once to pick that path up" \
systemd-tmpfiles --create --prefix=/nix/var/nix systemd-tmpfiles --create --prefix=/nix/var/nix

View file

@ -258,6 +258,8 @@ static int main_build_remote(int argc, char * * argv)
connected: connected:
close(5); close(5);
assert(sshStore);
std::cerr << "# accept\n" << storeUri << "\n"; std::cerr << "# accept\n" << storeUri << "\n";
auto inputs = readStrings<PathSet>(source); auto inputs = readStrings<PathSet>(source);
@ -286,23 +288,48 @@ connected:
uploadLock = -1; uploadLock = -1;
auto drv = store->readDerivation(*drvPath); auto drv = store->readDerivation(*drvPath);
std::optional<BuildResult> optResult;
// If we don't know whether we are trusted (e.g. `ssh://`
// stores), we assume we are. This is necessary for backwards
// compat.
bool trustedOrLegacy = ({
std::optional trusted = sshStore->isTrustedClient();
!trusted || *trusted;
});
// See the very large comment in `case wopBuildDerivation:` in
// `src/libstore/daemon.cc` that explains the trust model here.
//
// This condition mirrors that: that code enforces the "rules" outlined there;
// we do the best we can given those "rules".
if (trustedOrLegacy || drv.type().isCA()) {
// Hijack the inputs paths of the derivation to include all
// the paths that come from the `inputDrvs` set. We dont do
// that for the derivations whose `inputDrvs` is empty
// because:
//
// 1. Its not needed
//
// 2. Changing the `inputSrcs` set changes the associated
// output ids, which break CA derivations
if (!drv.inputDrvs.empty())
drv.inputSrcs = store->parseStorePathSet(inputs);
optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv);
auto & result = *optResult;
if (!result.success())
throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
} else {
copyClosure(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute);
auto res = sshStore->buildPathsWithResults({ DerivedPath::Built { *drvPath, OutputsSpec::All {} } });
// One path to build should produce exactly one build result
assert(res.size() == 1);
optResult = std::move(res[0]);
}
auto outputHashes = staticOutputHashes(*store, drv); auto outputHashes = staticOutputHashes(*store, drv);
// Hijack the inputs paths of the derivation to include all the paths
// that come from the `inputDrvs` set.
// We dont do that for the derivations whose `inputDrvs` is empty
// because
// 1. Its not needed
// 2. Changing the `inputSrcs` set changes the associated output ids,
// which break CA derivations
if (!drv.inputDrvs.empty())
drv.inputSrcs = store->parseStorePathSet(inputs);
auto result = sshStore->buildDerivation(*drvPath, drv);
if (!result.success())
throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
std::set<Realisation> missingRealisations; std::set<Realisation> missingRealisations;
StorePathSet missingPaths; StorePathSet missingPaths;
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) { if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
@ -311,6 +338,8 @@ connected:
auto thisOutputId = DrvOutput{ thisOutputHash, outputName }; auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
if (!store->queryRealisation(thisOutputId)) { if (!store->queryRealisation(thisOutputId)) {
debug("missing output %s", outputName); debug("missing output %s", outputName);
assert(optResult);
auto & result = *optResult;
auto i = result.builtOutputs.find(outputName); auto i = result.builtOutputs.find(outputName);
assert(i != result.builtOutputs.end()); assert(i != result.builtOutputs.end());
auto & newRealisation = i->second; auto & newRealisation = i->second;

View file

@ -121,6 +121,8 @@ ref<EvalState> EvalCommand::getEvalState()
#endif #endif
; ;
evalState->repair = repair;
if (startReplOnEvalErrors) { if (startReplOnEvalErrors) {
evalState->debugRepl = &AbstractNixRepl::runSimple; evalState->debugRepl = &AbstractNixRepl::runSimple;
}; };

View file

@ -153,7 +153,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
for (auto & i : autoArgs) { for (auto & i : autoArgs) {
auto v = state.allocValue(); auto v = state.allocValue();
if (i.second[0] == 'E') if (i.second[0] == 'E')
state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), absPath("."))); state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), state.rootPath(CanonPath::fromCwd())));
else else
v->mkString(((std::string_view) i.second).substr(1)); v->mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v); res.insert(state.symbols.create(i.first), v);
@ -161,19 +161,19 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish(); return res.finish();
} }
Path lookupFileArg(EvalState & state, std::string_view s) SourcePath lookupFileArg(EvalState & state, std::string_view s)
{ {
if (EvalSettings::isPseudoUrl(s)) { if (EvalSettings::isPseudoUrl(s)) {
auto storePath = fetchers::downloadTarball( auto storePath = fetchers::downloadTarball(
state.store, EvalSettings::resolvePseudoUrl(s), "source", false).first.storePath; state.store, EvalSettings::resolvePseudoUrl(s), "source", false).first.storePath;
return state.store->toRealPath(storePath); return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
} }
else if (hasPrefix(s, "flake:")) { else if (hasPrefix(s, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false); auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false);
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath; auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
return state.store->toRealPath(storePath); return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
} }
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
@ -182,7 +182,7 @@ Path lookupFileArg(EvalState & state, std::string_view s)
} }
else else
return absPath(std::string(s)); return state.rootPath(CanonPath::fromCwd(s));
} }
} }

View file

@ -2,14 +2,16 @@
///@file ///@file
#include "args.hh" #include "args.hh"
#include "common-args.hh"
namespace nix { namespace nix {
class Store; class Store;
class EvalState; class EvalState;
class Bindings; class Bindings;
struct SourcePath;
struct MixEvalArgs : virtual Args struct MixEvalArgs : virtual Args, virtual MixRepair
{ {
static constexpr auto category = "Common evaluation options"; static constexpr auto category = "Common evaluation options";
@ -25,6 +27,6 @@ private:
std::map<std::string, std::string> autoArgs; std::map<std::string, std::string> autoArgs;
}; };
Path lookupFileArg(EvalState & state, std::string_view s); SourcePath lookupFileArg(EvalState & state, std::string_view s);
} }

View file

@ -3,8 +3,11 @@
namespace nix { namespace nix {
Strings editorFor(const Path & file, uint32_t line) Strings editorFor(const SourcePath & file, uint32_t line)
{ {
auto path = file.getPhysicalPath();
if (!path)
throw Error("cannot open '%s' in an editor because it has no physical path", file);
auto editor = getEnv("EDITOR").value_or("cat"); auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString<Strings>(editor); auto args = tokenizeString<Strings>(editor);
if (line > 0 && ( if (line > 0 && (
@ -13,7 +16,7 @@ Strings editorFor(const Path & file, uint32_t line)
editor.find("vim") != std::string::npos || editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos)) editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line)); args.push_back(fmt("+%d", line));
args.push_back(file); args.push_back(path->abs());
return args; return args;
} }

View file

@ -2,6 +2,7 @@
///@file ///@file
#include "types.hh" #include "types.hh"
#include "input-accessor.hh"
namespace nix { namespace nix {
@ -9,6 +10,6 @@ namespace nix {
* Helper function to generate args that invoke $EDITOR on * Helper function to generate args that invoke $EDITOR on
* filename:lineno. * filename:lineno.
*/ */
Strings editorFor(const Path & file, uint32_t line); Strings editorFor(const SourcePath & file, uint32_t line);
} }

View file

@ -46,7 +46,15 @@ std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths() DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
{ {
auto v = toValue(*state).first; auto [v, pos] = toValue(*state);
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
*v,
pos,
fmt("while evaluating the attribute '%s'", attrPath)))
{
return { *derivedPathWithInfo };
}
Bindings & autoArgs = *cmd.getAutoArgs(*state); Bindings & autoArgs = *cmd.getAutoArgs(*state);

View file

@ -95,32 +95,13 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
// FIXME: use eval cache? // FIXME: use eval cache?
auto v = attr->forceValue(); auto v = attr->forceValue();
if (v.type() == nPath) { if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
PathSet context; v,
auto storePath = state->copyPathToStore(context, Path(v.path)); noPos,
return {{ fmt("while evaluating the flake output attribute '%s'", attrPath)))
.path = DerivedPath::Opaque { {
.path = std::move(storePath), return { *derivedPathWithInfo };
},
.info = make_ref<ExtraPathInfo>(),
}};
} }
else if (v.type() == nString) {
PathSet context;
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
auto storePath = state->store->maybeParseStorePath(s);
if (storePath && context.count(std::string(s))) {
return {{
.path = DerivedPath::Opaque {
.path = std::move(*storePath),
},
.info = make_ref<ExtraPathInfo>(),
}};
} else
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
}
else else
throw Error("flake output attribute '%s' is not a derivation or path", attrPath); throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
} }
@ -235,7 +216,7 @@ FlakeRef InstallableFlake::nixpkgsFlakeRef() const
} }
} }
return InstallableValue::nixpkgsFlakeRef(); return defaultNixpkgsFlakeRef();
} }
} }

View file

@ -67,9 +67,22 @@ struct InstallableFlake : InstallableValue
std::shared_ptr<flake::LockedFlake> getLockedFlake() const; std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override; FlakeRef nixpkgsFlakeRef() const;
}; };
/**
* Default flake ref for referring to Nixpkgs. For flakes that don't
* have their own Nixpkgs input, or other installables.
*
* It is a layer violation for Nix to know about Nixpkgs; currently just
* `nix develop` does. Be wary of using this /
* `InstallableFlake::nixpkgsFlakeRef` more places.
*/
static inline FlakeRef defaultNixpkgsFlakeRef()
{
return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}});
}
ref<eval_cache::EvalCache> openEvalCache( ref<eval_cache::EvalCache> openEvalCache(
EvalState & state, EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake); std::shared_ptr<flake::LockedFlake> lockedFlake);

View file

@ -41,4 +41,26 @@ ref<InstallableValue> InstallableValue::require(ref<Installable> installable)
return ref { castedInstallable }; return ref { castedInstallable };
} }
std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx)
{
if (v.type() == nPath) {
auto storePath = v.path().fetchToStore(state->store);
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
},
.info = make_ref<ExtraPathInfo>(),
}};
}
else if (v.type() == nString) {
return {{
.path = state->coerceToDerivedPath(pos, v, errorCtx),
.info = make_ref<ExtraPathInfo>(),
}};
}
else return std::nullopt;
}
} }

View file

@ -96,13 +96,26 @@ struct InstallableValue : Installable
UnresolvedApp toApp(EvalState & state); UnresolvedApp toApp(EvalState & state);
virtual FlakeRef nixpkgsFlakeRef() const
{
return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}});
}
static InstallableValue & require(Installable & installable); static InstallableValue & require(Installable & installable);
static ref<InstallableValue> require(ref<Installable> installable); static ref<InstallableValue> require(ref<Installable> installable);
protected:
/**
* Handles either a plain path, or a string with a single string
* context elem in the right format. The latter case is handled by
* `EvalState::coerceToDerivedPath()`; see it for details.
*
* @param v Value that is hopefully a string or path per the above.
*
* @param pos Position of value to aid with diagnostics.
*
* @param errorCtx Arbitrary message for use in potential error message when something is wrong with `v`.
*
* @result A derived path (with empty info, for now) if the value
* matched the above criteria.
*/
std::optional<DerivedPathWithInfo> trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx);
}; };
} }

View file

@ -449,7 +449,7 @@ Installables SourceExprCommand::parseInstallables(
else if (file) else if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile); state->evalFile(lookupFileArg(*state, *file), *vFile);
else { else {
auto e = state->parseExprFromString(*expr, absPath(".")); auto e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
state->eval(e, *vFile); state->eval(e, *vFile);
} }

View file

@ -55,8 +55,6 @@ struct NixRepl
, gc , gc
#endif #endif
{ {
std::string curDir;
size_t debugTraceIndex; size_t debugTraceIndex;
Strings loadedFiles; Strings loadedFiles;
@ -114,7 +112,6 @@ NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalStat
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get())) , staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
, historyFile(getDataDir() + "/nix/repl-history") , historyFile(getDataDir() + "/nix/repl-history")
{ {
curDir = absPath(".");
} }
@ -594,14 +591,14 @@ bool NixRepl::processLine(std::string line)
Value v; Value v;
evalString(arg, v); evalString(arg, v);
const auto [path, line] = [&] () -> std::pair<Path, uint32_t> { const auto [path, line] = [&] () -> std::pair<SourcePath, uint32_t> {
if (v.type() == nPath || v.type() == nString) { if (v.type() == nPath || v.type() == nString) {
PathSet context; NixStringContext context;
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit"); auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
return {path, 0}; return {path, 0};
} else if (v.isLambda()) { } else if (v.isLambda()) {
auto pos = state->positions[v.lambda.fun->pos]; auto pos = state->positions[v.lambda.fun->pos];
if (auto path = std::get_if<Path>(&pos.origin)) if (auto path = std::get_if<SourcePath>(&pos.origin))
return {*path, pos.line}; return {*path, pos.line};
else else
throw Error("'%s' cannot be shown in an editor", pos); throw Error("'%s' cannot be shown in an editor", pos);
@ -876,8 +873,7 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
Expr * NixRepl::parseString(std::string s) Expr * NixRepl::parseString(std::string s)
{ {
Expr * e = state->parseExprFromString(std::move(s), curDir, staticEnv); return state->parseExprFromString(std::move(s), state->rootPath(CanonPath::fromCwd()), staticEnv);
return e;
} }
@ -925,7 +921,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break; break;
case nPath: case nPath:
str << ANSI_GREEN << v.path << ANSI_NORMAL; // !!! escaping? str << ANSI_GREEN << v.path().to_string() << ANSI_NORMAL; // !!! escaping?
break; break;
case nNull: case nNull:
@ -940,7 +936,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
if (isDrv) { if (isDrv) {
str << "«derivation "; str << "«derivation ";
Bindings::iterator i = v.attrs->find(state->sDrvPath); Bindings::iterator i = v.attrs->find(state->sDrvPath);
PathSet context; NixStringContext context;
if (i != v.attrs->end()) if (i != v.attrs->end())
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
else else

View file

@ -106,7 +106,7 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
} }
std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what) std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
{ {
Value * v2; Value * v2;
try { try {
@ -118,21 +118,25 @@ std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value &
// FIXME: is it possible to extract the Pos object instead of doing this // FIXME: is it possible to extract the Pos object instead of doing this
// toString + parsing? // toString + parsing?
auto pos = state.forceString(*v2, noPos, "while evaluating the 'meta.position' attribute of a derivation"); NixStringContext context;
auto path = state.coerceToPath(noPos, *v2, context, "while evaluating the 'meta.position' attribute of a derivation");
auto colon = pos.rfind(':'); auto fn = path.path.abs();
if (colon == std::string::npos)
throw ParseError("cannot parse meta.position attribute '%s'", pos); auto fail = [fn]() {
throw ParseError("cannot parse 'meta.position' attribute '%s'", fn);
};
std::string filename(pos, 0, colon);
unsigned int lineno;
try { try {
lineno = std::stoi(std::string(pos, colon + 1, std::string::npos)); auto colon = fn.rfind(':');
if (colon == std::string::npos) fail();
std::string filename(fn, 0, colon);
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
return {CanonPath(fn.substr(0, colon)), lineno};
} catch (std::invalid_argument & e) { } catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos); fail();
abort();
} }
return { std::move(filename), lineno };
} }

View file

@ -20,7 +20,7 @@ std::pair<Value *, PosIdx> findAlongAttrPath(
/** /**
* Heuristic to find the filename and lineno or a nix value. * Heuristic to find the filename and lineno or a nix value.
*/ */
std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what); std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what);
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s); std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);

View file

@ -47,7 +47,7 @@ struct AttrDb
{ {
auto state(_state->lock()); auto state(_state->lock());
Path cacheDir = getCacheDir() + "/nix/eval-cache-v4"; Path cacheDir = getCacheDir() + "/nix/eval-cache-v5";
createDirs(cacheDir); createDirs(cacheDir);
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite"; Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
@ -300,7 +300,7 @@ struct AttrDb
NixStringContext context; NixStringContext context;
if (!queryAttribute.isNull(3)) if (!queryAttribute.isNull(3))
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";")) for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
context.push_back(NixStringContextElem::parse(cfg, s)); context.insert(NixStringContextElem::parse(s));
return {{rowId, string_t{queryAttribute.getStr(2), context}}}; return {{rowId, string_t{queryAttribute.getStr(2), context}}};
} }
case AttrType::Bool: case AttrType::Bool:
@ -442,8 +442,10 @@ Value & AttrCursor::forceValue()
if (v.type() == nString) if (v.type() == nString)
cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context), cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context),
string_t{v.string.s, {}}}; string_t{v.string.s, {}}};
else if (v.type() == nPath) else if (v.type() == nPath) {
cachedValue = {root->db->setString(getKey(), v.path), string_t{v.path, {}}}; auto path = v.path().path;
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
}
else if (v.type() == nBool) else if (v.type() == nBool)
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean}; cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
else if (v.type() == nInt) else if (v.type() == nInt)
@ -580,7 +582,7 @@ std::string AttrCursor::getString()
if (v.type() != nString && v.type() != nPath) if (v.type() != nString && v.type() != nPath)
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>(); root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
return v.type() == nString ? v.string.s : v.path; return v.type() == nString ? v.string.s : v.path().to_string();
} }
string_t AttrCursor::getStringWithContext() string_t AttrCursor::getStringWithContext()
@ -619,10 +621,13 @@ string_t AttrCursor::getStringWithContext()
auto & v = forceValue(); auto & v = forceValue();
if (v.type() == nString) if (v.type() == nString) {
return {v.string.s, v.getContext(*root->state.store)}; NixStringContext context;
copyContext(v, context);
return {v.string.s, std::move(context)};
}
else if (v.type() == nPath) else if (v.type() == nPath)
return {v.path, {}}; return {v.path().to_string(), {}};
else else
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>(); root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
} }

View file

@ -4,6 +4,7 @@
#include "util.hh" #include "util.hh"
#include "store-api.hh" #include "store-api.hh"
#include "derivations.hh" #include "derivations.hh"
#include "downstream-placeholder.hh"
#include "globals.hh" #include "globals.hh"
#include "eval-inline.hh" #include "eval-inline.hh"
#include "filetransfer.hh" #include "filetransfer.hh"
@ -94,7 +95,6 @@ RootValue allocRootValue(Value * v)
#endif #endif
} }
void Value::print(const SymbolTable & symbols, std::ostream & str, void Value::print(const SymbolTable & symbols, std::ostream & str,
std::set<const void *> * seen) const std::set<const void *> * seen) const
{ {
@ -111,7 +111,7 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
printLiteralString(str, string.s); printLiteralString(str, string.s);
break; break;
case tPath: case tPath:
str << path; // !!! escaping? str << path().to_string(); // !!! escaping?
break; break;
case tNull: case tNull:
str << "null"; str << "null";
@ -535,6 +535,7 @@ EvalState::EvalState(
, sOutputSpecified(symbols.create("outputSpecified")) , sOutputSpecified(symbols.create("outputSpecified"))
, repair(NoRepair) , repair(NoRepair)
, emptyBindings(0) , emptyBindings(0)
, derivationInternal(rootPath(CanonPath("/builtin/derivation.nix")))
, store(store) , store(store)
, buildStore(buildStore ? buildStore : store) , buildStore(buildStore ? buildStore : store)
, debugRepl(nullptr) , debugRepl(nullptr)
@ -609,15 +610,14 @@ void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value &
{ {
allowPath(storePath); allowPath(storePath);
auto path = store->printStorePath(storePath); mkStorePathString(storePath, v);
v.mkString(path, PathSet({path}));
} }
Path EvalState::checkSourcePath(const Path & path_) SourcePath EvalState::checkSourcePath(const SourcePath & path_)
{ {
if (!allowedPaths) return path_; if (!allowedPaths) return path_;
auto i = resolvedPaths.find(path_); auto i = resolvedPaths.find(path_.path.abs());
if (i != resolvedPaths.end()) if (i != resolvedPaths.end())
return i->second; return i->second;
@ -627,9 +627,9 @@ Path EvalState::checkSourcePath(const Path & path_)
* attacker can't append ../../... to a path that would be in allowedPaths * attacker can't append ../../... to a path that would be in allowedPaths
* and thus leak symlink targets. * and thus leak symlink targets.
*/ */
Path abspath = canonPath(path_); Path abspath = canonPath(path_.path.abs());
if (hasPrefix(abspath, corepkgsPrefix)) return abspath; if (hasPrefix(abspath, corepkgsPrefix)) return CanonPath(abspath);
for (auto & i : *allowedPaths) { for (auto & i : *allowedPaths) {
if (isDirOrInDir(abspath, i)) { if (isDirOrInDir(abspath, i)) {
@ -647,11 +647,11 @@ Path EvalState::checkSourcePath(const Path & path_)
/* Resolve symlinks. */ /* Resolve symlinks. */
debug("checking access to '%s'", abspath); debug("checking access to '%s'", abspath);
Path path = canonPath(abspath, true); SourcePath path = CanonPath(canonPath(abspath, true));
for (auto & i : *allowedPaths) { for (auto & i : *allowedPaths) {
if (isDirOrInDir(path, i)) { if (isDirOrInDir(path.path.abs(), i)) {
resolvedPaths[path_] = path; resolvedPaths.insert_or_assign(path_.path.abs(), path);
return path; return path;
} }
} }
@ -679,12 +679,12 @@ void EvalState::checkURI(const std::string & uri)
/* If the URI is a path, then check it against allowedPaths as /* If the URI is a path, then check it against allowedPaths as
well. */ well. */
if (hasPrefix(uri, "/")) { if (hasPrefix(uri, "/")) {
checkSourcePath(uri); checkSourcePath(CanonPath(uri));
return; return;
} }
if (hasPrefix(uri, "file://")) { if (hasPrefix(uri, "file://")) {
checkSourcePath(std::string(uri, 7)); checkSourcePath(CanonPath(std::string(uri, 7)));
return; return;
} }
@ -692,7 +692,7 @@ void EvalState::checkURI(const std::string & uri)
} }
Path EvalState::toRealPath(const Path & path, const PathSet & context) Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
{ {
// FIXME: check whether 'path' is in 'context'. // FIXME: check whether 'path' is in 'context'.
return return
@ -944,34 +944,34 @@ void Value::mkString(std::string_view s)
} }
static void copyContextToValue(Value & v, const PathSet & context) static void copyContextToValue(Value & v, const NixStringContext & context)
{ {
if (!context.empty()) { if (!context.empty()) {
size_t n = 0; size_t n = 0;
v.string.context = (const char * *) v.string.context = (const char * *)
allocBytes((context.size() + 1) * sizeof(char *)); allocBytes((context.size() + 1) * sizeof(char *));
for (auto & i : context) for (auto & i : context)
v.string.context[n++] = dupString(i.c_str()); v.string.context[n++] = dupString(i.to_string().c_str());
v.string.context[n] = 0; v.string.context[n] = 0;
} }
} }
void Value::mkString(std::string_view s, const PathSet & context) void Value::mkString(std::string_view s, const NixStringContext & context)
{ {
mkString(s); mkString(s);
copyContextToValue(*this, context); copyContextToValue(*this, context);
} }
void Value::mkStringMove(const char * s, const PathSet & context) void Value::mkStringMove(const char * s, const NixStringContext & context)
{ {
mkString(s); mkString(s);
copyContextToValue(*this, context); copyContextToValue(*this, context);
} }
void Value::mkPath(std::string_view s) void Value::mkPath(const SourcePath & path)
{ {
mkPath(makeImmutableString(s)); mkPath(makeImmutableString(path.path.abs()));
} }
@ -1027,9 +1027,9 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
void EvalState::mkPos(Value & v, PosIdx p) void EvalState::mkPos(Value & v, PosIdx p)
{ {
auto pos = positions[p]; auto pos = positions[p];
if (auto path = std::get_if<Path>(&pos.origin)) { if (auto path = std::get_if<SourcePath>(&pos.origin)) {
auto attrs = buildBindings(3); auto attrs = buildBindings(3);
attrs.alloc(sFile).mkString(*path); attrs.alloc(sFile).mkString(path->path.abs());
attrs.alloc(sLine).mkInt(pos.line); attrs.alloc(sLine).mkInt(pos.line);
attrs.alloc(sColumn).mkInt(pos.column); attrs.alloc(sColumn).mkInt(pos.column);
v.mkAttrs(attrs); v.mkAttrs(attrs);
@ -1038,6 +1038,37 @@ void EvalState::mkPos(Value & v, PosIdx p)
} }
void EvalState::mkStorePathString(const StorePath & p, Value & v)
{
v.mkString(
store->printStorePath(p),
NixStringContext {
NixStringContextElem::Opaque { .path = p },
});
}
void EvalState::mkOutputString(
Value & value,
const StorePath & drvPath,
const std::string outputName,
std::optional<StorePath> optOutputPath)
{
value.mkString(
optOutputPath
? store->printStorePath(*std::move(optOutputPath))
/* Downstream we would substitute this for an actual path once
we build the floating CA derivation */
: DownstreamPlaceholder::unknownCaOutput(drvPath, outputName).render(),
NixStringContext {
NixStringContextElem::Built {
.drvPath = drvPath,
.output = outputName,
}
});
}
/* Create a thunk for the delayed computation of the given expression /* Create a thunk for the delayed computation of the given expression
in the given environment. But if the expression is a variable, in the given environment. But if the expression is a variable,
then look it up right away. This significantly reduces the number then look it up right away. This significantly reduces the number
@ -1085,7 +1116,7 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
} }
void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial) void EvalState::evalFile(const SourcePath & path_, Value & v, bool mustBeTrivial)
{ {
auto path = checkSourcePath(path_); auto path = checkSourcePath(path_);
@ -1095,7 +1126,7 @@ void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial)
return; return;
} }
Path resolvedPath = resolveExprPath(path); auto resolvedPath = resolveExprPath(path);
if ((i = fileEvalCache.find(resolvedPath)) != fileEvalCache.end()) { if ((i = fileEvalCache.find(resolvedPath)) != fileEvalCache.end()) {
v = i->second; v = i->second;
return; return;
@ -1123,8 +1154,8 @@ void EvalState::resetFileCache()
void EvalState::cacheFile( void EvalState::cacheFile(
const Path & path, const SourcePath & path,
const Path & resolvedPath, const SourcePath & resolvedPath,
Expr * e, Expr * e,
Value & v, Value & v,
bool mustBeTrivial) bool mustBeTrivial)
@ -1138,7 +1169,7 @@ void EvalState::cacheFile(
*e, *e,
this->baseEnv, this->baseEnv,
e->getPos() ? static_cast<std::shared_ptr<AbstractPos>>(positions[e->getPos()]) : nullptr, e->getPos() ? static_cast<std::shared_ptr<AbstractPos>>(positions[e->getPos()]) : nullptr,
"while evaluating the file '%1%':", resolvedPath) "while evaluating the file '%1%':", resolvedPath.to_string())
: nullptr; : nullptr;
// Enforce that 'flake.nix' is a direct attrset, not a // Enforce that 'flake.nix' is a direct attrset, not a
@ -1148,7 +1179,7 @@ void EvalState::cacheFile(
error("file '%s' must be an attribute set", path).debugThrow<EvalError>(); error("file '%s' must be an attribute set", path).debugThrow<EvalError>();
eval(e, v); eval(e, v);
} catch (Error & e) { } catch (Error & e) {
addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath); addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath.to_string());
throw; throw;
} }
@ -1409,8 +1440,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} catch (Error & e) { } catch (Error & e) {
if (pos2) { if (pos2) {
auto pos2r = state.positions[pos2]; auto pos2r = state.positions[pos2];
auto origin = std::get_if<Path>(&pos2r.origin); auto origin = std::get_if<SourcePath>(&pos2r.origin);
if (!(origin && *origin == state.derivationNixPath)) if (!(origin && *origin == state.derivationInternal))
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'", state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
showAttrPath(state, env, attrPath)); showAttrPath(state, env, attrPath));
} }
@ -1900,7 +1931,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{ {
PathSet context; NixStringContext context;
std::vector<BackedStringView> s; std::vector<BackedStringView> s;
size_t sSize = 0; size_t sSize = 0;
NixInt n = 0; NixInt n = 0;
@ -1983,7 +2014,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
else if (firstType == nPath) { else if (firstType == nPath) {
if (!context.empty()) if (!context.empty())
state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow<EvalError>(); state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow<EvalError>();
v.mkPath(canonPath(str())); v.mkPath(CanonPath(canonPath(str())));
} else } else
v.mkStringMove(c_str(), context); v.mkStringMove(c_str(), context);
} }
@ -2109,26 +2140,15 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
} }
void copyContext(const Value & v, PathSet & context) void copyContext(const Value & v, NixStringContext & context)
{ {
if (v.string.context) if (v.string.context)
for (const char * * p = v.string.context; *p; ++p) for (const char * * p = v.string.context; *p; ++p)
context.insert(*p); context.insert(NixStringContextElem::parse(*p));
} }
NixStringContext Value::getContext(const Store & store) std::string_view EvalState::forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx)
{
NixStringContext res;
assert(internalType == tString);
if (string.context)
for (const char * * p = string.context; *p; ++p)
res.push_back(NixStringContextElem::parse(store, *p));
return res;
}
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx)
{ {
auto s = forceString(v, pos, errorCtx); auto s = forceString(v, pos, errorCtx);
copyContext(v, context); copyContext(v, context);
@ -2158,7 +2178,7 @@ bool EvalState::isDerivation(Value & v)
std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v, std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore, bool copyToStore) NixStringContext & context, bool coerceMore, bool copyToStore)
{ {
auto i = v.attrs->find(sToString); auto i = v.attrs->find(sToString);
if (i != v.attrs->end()) { if (i != v.attrs->end()) {
@ -2172,8 +2192,14 @@ std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value &
return {}; return {};
} }
BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &context, BackedStringView EvalState::coerceToString(
std::string_view errorCtx, bool coerceMore, bool copyToStore, bool canonicalizePath) const PosIdx pos,
Value & v,
NixStringContext & context,
std::string_view errorCtx,
bool coerceMore,
bool copyToStore,
bool canonicalizePath)
{ {
forceValue(v, pos); forceValue(v, pos);
@ -2183,12 +2209,14 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &
} }
if (v.type() == nPath) { if (v.type() == nPath) {
BackedStringView path(PathView(v.path)); return
if (canonicalizePath) !canonicalizePath && !copyToStore
path = canonPath(*path); ? // FIXME: hack to preserve path literals that end in a
if (copyToStore) // slash, as in /foo/${x}.
path = store->printStorePath(copyPathToStore(context, std::move(path).toOwned())); v._path
return path; : copyToStore
? store->printStorePath(copyPathToStore(context, v.path()))
: std::string(v.path().path.abs());
} }
if (v.type() == nAttrs) { if (v.type() == nAttrs) {
@ -2249,40 +2277,40 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &
} }
StorePath EvalState::copyPathToStore(PathSet & context, const Path & path) StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePath & path)
{ {
if (nix::isDerivation(path)) if (nix::isDerivation(path.path.abs()))
error("file names are not allowed to end in '%1%'", drvExtension).debugThrow<EvalError>(); error("file names are not allowed to end in '%1%'", drvExtension).debugThrow<EvalError>();
auto dstPath = [&]() -> StorePath auto i = srcToStore.find(path);
{
auto i = srcToStore.find(path);
if (i != srcToStore.end()) return i->second;
auto dstPath = settings.readOnlyMode auto dstPath = i != srcToStore.end()
? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first ? i->second
: store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair); : [&]() {
allowPath(dstPath); auto dstPath = path.fetchToStore(store, path.baseName(), nullptr, repair);
srcToStore.insert_or_assign(path, dstPath); allowPath(dstPath);
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath)); srcToStore.insert_or_assign(path, dstPath);
return dstPath; printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
}(); return dstPath;
}();
context.insert(store->printStorePath(dstPath)); context.insert(NixStringContextElem::Opaque {
.path = dstPath
});
return dstPath; return dstPath;
} }
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx) SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)
{ {
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned(); auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
if (path == "" || path[0] != '/') if (path == "" || path[0] != '/')
error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow<EvalError>(); error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
return path; return CanonPath(path);
} }
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx) StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)
{ {
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned(); auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
if (auto storePath = store->maybeParseStorePath(path)) if (auto storePath = store->maybeParseStorePath(path))
@ -2291,6 +2319,80 @@ StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & co
} }
std::pair<DerivedPath, std::string_view> EvalState::coerceToDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx)
{
NixStringContext context;
auto s = forceString(v, context, pos, errorCtx);
auto csize = context.size();
if (csize != 1)
error(
"string '%s' has %d entries in its context. It should only have exactly one entry",
s, csize)
.withTrace(pos, errorCtx).debugThrow<EvalError>();
auto derivedPath = std::visit(overloaded {
[&](NixStringContextElem::Opaque && o) -> DerivedPath {
return DerivedPath::Opaque {
.path = std::move(o.path),
};
},
[&](NixStringContextElem::DrvDeep &&) -> DerivedPath {
error(
"string '%s' has a context which refers to a complete source and binary closure. This is not supported at this time",
s).withTrace(pos, errorCtx).debugThrow<EvalError>();
},
[&](NixStringContextElem::Built && b) -> DerivedPath {
return DerivedPath::Built {
.drvPath = std::move(b.drvPath),
.outputs = OutputsSpec::Names { std::move(b.output) },
};
},
}, ((NixStringContextElem &&) *context.begin()).raw());
return {
std::move(derivedPath),
std::move(s),
};
}
DerivedPath EvalState::coerceToDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx)
{
auto [derivedPath, s_] = coerceToDerivedPathUnchecked(pos, v, errorCtx);
auto s = s_;
std::visit(overloaded {
[&](const DerivedPath::Opaque & o) {
auto sExpected = store->printStorePath(o.path);
if (s != sExpected)
error(
"path string '%s' has context with the different path '%s'",
s, sExpected)
.withTrace(pos, errorCtx).debugThrow<EvalError>();
},
[&](const DerivedPath::Built & b) {
// TODO need derived path with single output to make this
// total. Will add as part of RFC 92 work and then this is
// cleaned up.
auto output = *std::get<OutputsSpec::Names>(b.outputs).begin();
auto drv = store->readDerivation(b.drvPath);
auto i = drv.outputs.find(output);
if (i == drv.outputs.end())
throw Error("derivation '%s' does not have output '%s'", store->printStorePath(b.drvPath), output);
auto optOutputPath = i->second.path(*store, drv.name, output);
// This is testing for the case of CA derivations
auto sExpected = optOutputPath
? store->printStorePath(*optOutputPath)
: DownstreamPlaceholder::unknownCaOutput(b.drvPath, output).render();
if (s != sExpected)
error(
"string '%s' has context with the output '%s' from derivation '%s', but the string is not the right placeholder for this derivation output. It should be '%s'",
s, output, store->printStorePath(b.drvPath), sExpected)
.withTrace(pos, errorCtx).debugThrow<EvalError>();
}
}, derivedPath.raw());
return derivedPath;
}
bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx) bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx)
{ {
forceValue(v1, noPos); forceValue(v1, noPos);
@ -2321,7 +2423,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
return strcmp(v1.string.s, v2.string.s) == 0; return strcmp(v1.string.s, v2.string.s) == 0;
case nPath: case nPath:
return strcmp(v1.path, v2.path) == 0; return strcmp(v1._path, v2._path) == 0;
case nNull: case nNull:
return true; return true;
@ -2448,8 +2550,8 @@ void EvalState::printStats()
else else
obj["name"] = nullptr; obj["name"] = nullptr;
if (auto pos = positions[fun->pos]) { if (auto pos = positions[fun->pos]) {
if (auto path = std::get_if<Path>(&pos.origin)) if (auto path = std::get_if<SourcePath>(&pos.origin))
obj["file"] = *path; obj["file"] = path->to_string();
obj["line"] = pos.line; obj["line"] = pos.line;
obj["column"] = pos.column; obj["column"] = pos.column;
} }
@ -2463,8 +2565,8 @@ void EvalState::printStats()
for (auto & i : attrSelects) { for (auto & i : attrSelects) {
json obj = json::object(); json obj = json::object();
if (auto pos = positions[i.first]) { if (auto pos = positions[i.first]) {
if (auto path = std::get_if<Path>(&pos.origin)) if (auto path = std::get_if<SourcePath>(&pos.origin))
obj["file"] = *path; obj["file"] = path->to_string();
obj["line"] = pos.line; obj["line"] = pos.line;
obj["column"] = pos.column; obj["column"] = pos.column;
} }
@ -2489,7 +2591,7 @@ void EvalState::printStats()
} }
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const std::string ExternalValueBase::coerceToString(const Pos & pos, NixStringContext & context, bool copyMore, bool copyToStore) const
{ {
throw TypeError({ throw TypeError({
.msg = hintfmt("cannot coerce %1% to a string", showType()) .msg = hintfmt("cannot coerce %1% to a string", showType())
@ -2518,7 +2620,7 @@ Strings EvalSettings::getDefaultNixPath()
{ {
Strings res; Strings res;
auto add = [&](const Path & p, const std::string & s = std::string()) { auto add = [&](const Path & p, const std::string & s = std::string()) {
if (pathExists(p)) { if (pathAccessible(p)) {
if (s.empty()) { if (s.empty()) {
res.push_back(p); res.push_back(p);
} else { } else {

View file

@ -8,6 +8,7 @@
#include "symbol-table.hh" #include "symbol-table.hh"
#include "config.hh" #include "config.hh"
#include "experimental-features.hh" #include "experimental-features.hh"
#include "input-accessor.hh"
#include <map> #include <map>
#include <optional> #include <optional>
@ -20,6 +21,7 @@ namespace nix {
class Store; class Store;
class EvalState; class EvalState;
class StorePath; class StorePath;
struct DerivedPath;
enum RepairFlag : bool; enum RepairFlag : bool;
@ -56,20 +58,14 @@ void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env &
std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env); std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env);
void copyContext(const Value & v, PathSet & context); void copyContext(const Value & v, NixStringContext & context);
/**
* Cache for calls to addToStore(); maps source paths to the store
* paths.
*/
typedef std::map<Path, StorePath> SrcToStore;
std::string printValue(const EvalState & state, const Value & v); std::string printValue(const EvalState & state, const Value & v);
std::ostream & operator << (std::ostream & os, const ValueType t); std::ostream & operator << (std::ostream & os, const ValueType t);
// FIXME: maybe change this to an std::variant<SourcePath, URL>.
typedef std::pair<std::string, std::string> SearchPathElem; typedef std::pair<std::string, std::string> SearchPathElem;
typedef std::list<SearchPathElem> SearchPath; typedef std::list<SearchPathElem> SearchPath;
@ -137,8 +133,6 @@ public:
SymbolTable symbols; SymbolTable symbols;
PosTable positions; PosTable positions;
static inline std::string derivationNixPath = "//builtin/derivation.nix";
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString, sFile, sLine, sColumn, sFunctor, sToString,
@ -149,7 +143,6 @@ public:
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath, sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
sPrefix, sPrefix,
sOutputSpecified; sOutputSpecified;
Symbol sDerivationNix;
/** /**
* If set, force copying files to the Nix store even if they * If set, force copying files to the Nix store even if they
@ -165,6 +158,8 @@ public:
Bindings emptyBindings; Bindings emptyBindings;
const SourcePath derivationInternal;
/** /**
* Store used to materialise .drv files. * Store used to materialise .drv files.
*/ */
@ -234,15 +229,18 @@ public:
} }
private: private:
SrcToStore srcToStore;
/* Cache for calls to addToStore(); maps source paths to the store
paths. */
std::map<SourcePath, StorePath> srcToStore;
/** /**
* A cache from path names to parse trees. * A cache from path names to parse trees.
*/ */
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
typedef std::map<Path, Expr *, std::less<Path>, traceable_allocator<std::pair<const Path, Expr *>>> FileParseCache; typedef std::map<SourcePath, Expr *, std::less<SourcePath>, traceable_allocator<std::pair<const SourcePath, Expr *>>> FileParseCache;
#else #else
typedef std::map<Path, Expr *> FileParseCache; typedef std::map<SourcePath, Expr *> FileParseCache;
#endif #endif
FileParseCache fileParseCache; FileParseCache fileParseCache;
@ -250,9 +248,9 @@ private:
* A cache from path names to values. * A cache from path names to values.
*/ */
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value>>> FileEvalCache; typedef std::map<SourcePath, Value, std::less<SourcePath>, traceable_allocator<std::pair<const SourcePath, Value>>> FileEvalCache;
#else #else
typedef std::map<Path, Value> FileEvalCache; typedef std::map<SourcePath, Value> FileEvalCache;
#endif #endif
FileEvalCache fileEvalCache; FileEvalCache fileEvalCache;
@ -263,7 +261,7 @@ private:
/** /**
* Cache used by checkSourcePath(). * Cache used by checkSourcePath().
*/ */
std::unordered_map<Path, Path> resolvedPaths; std::unordered_map<Path, SourcePath> resolvedPaths;
/** /**
* Cache used by prim_match(). * Cache used by prim_match().
@ -294,6 +292,12 @@ public:
SearchPath getSearchPath() { return searchPath; } SearchPath getSearchPath() { return searchPath; }
/**
* Return a `SourcePath` that refers to `path` in the root
* filesystem.
*/
SourcePath rootPath(CanonPath path);
/** /**
* Allow access to a path. * Allow access to a path.
*/ */
@ -314,7 +318,7 @@ public:
* Check whether access to a path is allowed and throw an error if * Check whether access to a path is allowed and throw an error if
* not. Otherwise return the canonicalised path. * not. Otherwise return the canonicalised path.
*/ */
Path checkSourcePath(const Path & path); SourcePath checkSourcePath(const SourcePath & path);
void checkURI(const std::string & uri); void checkURI(const std::string & uri);
@ -327,19 +331,19 @@ public:
* intended to distinguish between import-from-derivation and * intended to distinguish between import-from-derivation and
* sources stored in the actual /nix/store. * sources stored in the actual /nix/store.
*/ */
Path toRealPath(const Path & path, const PathSet & context); Path toRealPath(const Path & path, const NixStringContext & context);
/** /**
* Parse a Nix expression from the specified file. * Parse a Nix expression from the specified file.
*/ */
Expr * parseExprFromFile(const Path & path); Expr * parseExprFromFile(const SourcePath & path);
Expr * parseExprFromFile(const Path & path, std::shared_ptr<StaticEnv> & staticEnv); Expr * parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv);
/** /**
* Parse a Nix expression from the specified string. * Parse a Nix expression from the specified string.
*/ */
Expr * parseExprFromString(std::string s, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv); Expr * parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseExprFromString(std::string s, const Path & basePath); Expr * parseExprFromString(std::string s, const SourcePath & basePath);
Expr * parseStdin(); Expr * parseStdin();
@ -348,14 +352,14 @@ public:
* form. Optionally enforce that the top-level expression is * form. Optionally enforce that the top-level expression is
* trivial (i.e. doesn't require arbitrary computation). * trivial (i.e. doesn't require arbitrary computation).
*/ */
void evalFile(const Path & path, Value & v, bool mustBeTrivial = false); void evalFile(const SourcePath & path, Value & v, bool mustBeTrivial = false);
/** /**
* Like `evalFile`, but with an already parsed expression. * Like `evalFile`, but with an already parsed expression.
*/ */
void cacheFile( void cacheFile(
const Path & path, const SourcePath & path,
const Path & resolvedPath, const SourcePath & resolvedPath,
Expr * e, Expr * e,
Value & v, Value & v,
bool mustBeTrivial = false); bool mustBeTrivial = false);
@ -365,8 +369,8 @@ public:
/** /**
* Look up a file in the search path. * Look up a file in the search path.
*/ */
Path findFile(const std::string_view path); SourcePath findFile(const std::string_view path);
Path findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos); SourcePath findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/** /**
* If the specified search path element is a URI, download it. * If the specified search path element is a URI, download it.
@ -423,7 +427,7 @@ public:
*/ */
void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx); void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx);
std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx); std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx); std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx);
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx); std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
[[gnu::noinline]] [[gnu::noinline]]
@ -439,7 +443,7 @@ public:
bool isDerivation(Value & v); bool isDerivation(Value & v);
std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v, std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true); NixStringContext & context, bool coerceMore = false, bool copyToStore = true);
/** /**
* String coercion. * String coercion.
@ -449,12 +453,12 @@ public:
* booleans and lists to a string. If `copyToStore` is set, * booleans and lists to a string. If `copyToStore` is set,
* referenced paths are copied to the Nix store as a side effect. * referenced paths are copied to the Nix store as a side effect.
*/ */
BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context, BackedStringView coerceToString(const PosIdx pos, Value & v, NixStringContext & context,
std::string_view errorCtx, std::string_view errorCtx,
bool coerceMore = false, bool copyToStore = true, bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true); bool canonicalizePath = true);
StorePath copyPathToStore(PathSet & context, const Path & path); StorePath copyPathToStore(NixStringContext & context, const SourcePath & path);
/** /**
* Path coercion. * Path coercion.
@ -463,12 +467,34 @@ public:
* path. The result is guaranteed to be a canonicalised, absolute * path. The result is guaranteed to be a canonicalised, absolute
* path. Nothing is copied to the store. * path. Nothing is copied to the store.
*/ */
Path coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx); SourcePath coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx);
/** /**
* Like coerceToPath, but the result must be a store path. * Like coerceToPath, but the result must be a store path.
*/ */
StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx); StorePath coerceToStorePath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx);
/**
* Part of `coerceToDerivedPath()` without any store IO which is exposed for unit testing only.
*/
std::pair<DerivedPath, std::string_view> coerceToDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx);
/**
* Coerce to `DerivedPath`.
*
* Must be a string which is either a literal store path or a
* "placeholder (see `DownstreamPlaceholder`).
*
* Even more importantly, the string context must be exactly one
* element, which is either a `NixStringContextElem::Opaque` or
* `NixStringContextElem::Built`. (`NixStringContextEleme::DrvDeep`
* is not permitted).
*
* The string is parsed based on the context --- the context is the
* source of truth, and ultimately tells us what we want, and then
* we ensure the string corresponds to it.
*/
DerivedPath coerceToDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx);
public: public:
@ -525,7 +551,7 @@ private:
char * text, char * text,
size_t length, size_t length,
Pos::Origin origin, Pos::Origin origin,
Path basePath, const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv); std::shared_ptr<StaticEnv> & staticEnv);
public: public:
@ -573,6 +599,37 @@ public:
void mkThunk_(Value & v, Expr * expr); void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, PosIdx pos); void mkPos(Value & v, PosIdx pos);
/**
* Create a string representing a store path.
*
* The string is the printed store path with a context containing a single
* `NixStringContextElem::Opaque` element of that store path.
*/
void mkStorePathString(const StorePath & storePath, Value & v);
/**
* Create a string representing a `DerivedPath::Built`.
*
* The string is the printed store path with a context containing a single
* `NixStringContextElem::Built` element of the drv path and output name.
*
* @param value Value we are settings
*
* @param drvPath Path the drv whose output we are making a string for
*
* @param outputName Name of the output
*
* @param optOutputPath Optional output path for that string. Must
* be passed if and only if output store object is input-addressed.
* Will be printed to form string if passed, otherwise a placeholder
* will be used (see `DownstreamPlaceholder`).
*/
void mkOutputString(
Value & value,
const StorePath & drvPath,
const std::string outputName,
std::optional<StorePath> optOutputPath);
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx); void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
/** /**
@ -584,7 +641,7 @@ public:
* Realise the given context, and return a mapping from the placeholders * Realise the given context, and return a mapping from the placeholders
* used to construct the associated value to their final store path * used to construct the associated value to their final store path
*/ */
[[nodiscard]] StringMap realiseContext(const PathSet & context); [[nodiscard]] StringMap realiseContext(const NixStringContext & context);
private: private:
@ -650,7 +707,7 @@ std::string showType(const Value & v);
/** /**
* If `path` refers to a directory, then append "/default.nix". * If `path` refers to a directory, then append "/default.nix".
*/ */
Path resolveExprPath(Path path); SourcePath resolveExprPath(const SourcePath & path);
struct InvalidPathError : EvalError struct InvalidPathError : EvalError
{ {
@ -688,7 +745,13 @@ struct EvalSettings : Config
)"}; )"};
Setting<bool> pureEval{this, false, "pure-eval", Setting<bool> pureEval{this, false, "pure-eval",
"Whether to restrict file system and network access to files specified by cryptographic hash."}; R"(
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
- Restrict file system and network access to files specified by cryptographic hash
- Disable [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
)"
};
Setting<bool> enableImportFromDerivation{ Setting<bool> enableImportFromDerivation{
this, true, "allow-import-from-derivation", this, true, "allow-import-from-derivation",

View file

@ -222,9 +222,9 @@ static Flake getFlake(
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir); throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir);
Value vInfo; Value vInfo;
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack state.evalFile(CanonPath(flakeFile), vInfo, true); // FIXME: symlink attack
expectType(state, nAttrs, vInfo, state.positions.add({flakeFile}, 1, 1)); expectType(state, nAttrs, vInfo, state.positions.add({CanonPath(flakeFile)}, 1, 1));
if (auto description = vInfo.attrs->get(state.sDescription)) { if (auto description = vInfo.attrs->get(state.sDescription)) {
expectType(state, nString, *description->value, description->pos); expectType(state, nString, *description->value, description->pos);
@ -265,7 +265,7 @@ static Flake getFlake(
state.symbols[setting.name], state.symbols[setting.name],
std::string(state.forceStringNoCtx(*setting.value, setting.pos, ""))); std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
else if (setting.value->type() == nPath) { else if (setting.value->type() == nPath) {
PathSet emptyContext = {}; NixStringContext emptyContext = {};
flake.config.settings.emplace( flake.config.settings.emplace(
state.symbols[setting.name], state.symbols[setting.name],
state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned()); state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned());
@ -745,7 +745,7 @@ void callFlake(EvalState & state,
state.vCallFlake = allocRootValue(state.allocValue()); state.vCallFlake = allocRootValue(state.allocValue());
state.eval(state.parseExprFromString( state.eval(state.parseExprFromString(
#include "call-flake.nix.gen.hh" #include "call-flake.nix.gen.hh"
, "/"), **state.vCallFlake); , CanonPath::root), **state.vCallFlake);
} }
state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos); state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos);

View file

@ -71,7 +71,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath() const
{ {
if (!drvPath && attrs) { if (!drvPath && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath); Bindings::iterator i = attrs->find(state->sDrvPath);
PathSet context; NixStringContext context;
if (i == attrs->end()) if (i == attrs->end())
drvPath = {std::nullopt}; drvPath = {std::nullopt};
else else
@ -93,7 +93,7 @@ StorePath DrvInfo::queryOutPath() const
{ {
if (!outPath && attrs) { if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath); Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context; NixStringContext context;
if (i != attrs->end()) if (i != attrs->end())
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation"); outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
} }
@ -124,7 +124,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
/* And evaluate its outPath attribute. */ /* And evaluate its outPath attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
PathSet context; NixStringContext context;
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation")); outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
} else } else
outputs.emplace(output, std::nullopt); outputs.emplace(output, std::nullopt);

View file

@ -32,9 +32,9 @@ struct PosAdapter : AbstractPos
// Get rid of the null terminators added by the parser. // Get rid of the null terminators added by the parser.
return std::string(s.source->c_str()); return std::string(s.source->c_str());
}, },
[](const Path & path) -> std::optional<std::string> { [](const SourcePath & path) -> std::optional<std::string> {
try { try {
return readFile(path); return path.readFile();
} catch (Error &) { } catch (Error &) {
return std::nullopt; return std::nullopt;
} }
@ -48,7 +48,7 @@ struct PosAdapter : AbstractPos
[&](const Pos::none_tag &) { out << "«none»"; }, [&](const Pos::none_tag &) { out << "«none»"; },
[&](const Pos::Stdin &) { out << "«stdin»"; }, [&](const Pos::Stdin &) { out << "«stdin»"; },
[&](const Pos::String & s) { out << "«string»"; }, [&](const Pos::String & s) { out << "«string»"; },
[&](const Path & path) { out << path; } [&](const SourcePath & path) { out << path; }
}, origin); }, origin);
} }
}; };

View file

@ -34,7 +34,7 @@ struct Pos
struct Stdin { ref<std::string> source; }; struct Stdin { ref<std::string> source; };
struct String { ref<std::string> source; }; struct String { ref<std::string> source; };
typedef std::variant<none_tag, Stdin, String, Path> Origin; typedef std::variant<none_tag, Stdin, String, SourcePath> Origin;
Origin origin; Origin origin;

View file

@ -31,7 +31,7 @@ namespace nix {
EvalState & state; EvalState & state;
SymbolTable & symbols; SymbolTable & symbols;
Expr * result; Expr * result;
Path basePath; SourcePath basePath;
PosTable::Origin origin; PosTable::Origin origin;
std::optional<ErrorInfo> error; std::optional<ErrorInfo> error;
}; };
@ -509,7 +509,7 @@ string_parts_interpolated
path_start path_start
: PATH { : PATH {
Path path(absPath({$1.p, $1.l}, data->basePath)); Path path(absPath({$1.p, $1.l}, data->basePath.path.abs()));
/* add back in the trailing '/' to the first segment */ /* add back in the trailing '/' to the first segment */
if ($1.p[$1.l-1] == '/' && $1.l > 1) if ($1.p[$1.l-1] == '/' && $1.l > 1)
path += "/"; path += "/";
@ -651,7 +651,7 @@ Expr * EvalState::parse(
char * text, char * text,
size_t length, size_t length,
Pos::Origin origin, Pos::Origin origin,
Path basePath, const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv) std::shared_ptr<StaticEnv> & staticEnv)
{ {
yyscan_t scanner; yyscan_t scanner;
@ -675,48 +675,36 @@ Expr * EvalState::parse(
} }
Path resolveExprPath(Path path) SourcePath resolveExprPath(const SourcePath & path)
{ {
assert(path[0] == '/');
unsigned int followCount = 0, maxFollow = 1024;
/* If `path' is a symlink, follow it. This is so that relative /* If `path' is a symlink, follow it. This is so that relative
path references work. */ path references work. */
struct stat st; 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);
st = lstat(path);
if (!S_ISLNK(st.st_mode)) break;
path = absPath(readLink(path), dirOf(path));
}
/* If `path' refers to a directory, append `/default.nix'. */ /* If `path' refers to a directory, append `/default.nix'. */
if (S_ISDIR(st.st_mode)) if (path2.lstat().type == InputAccessor::tDirectory)
path = canonPath(path + "/default.nix"); return path2 + "default.nix";
return path; return path2;
} }
Expr * EvalState::parseExprFromFile(const Path & path) Expr * EvalState::parseExprFromFile(const SourcePath & path)
{ {
return parseExprFromFile(path, staticBaseEnv); return parseExprFromFile(path, staticBaseEnv);
} }
Expr * EvalState::parseExprFromFile(const Path & path, std::shared_ptr<StaticEnv> & staticEnv) Expr * EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv)
{ {
auto buffer = readFile(path); auto buffer = path.readFile();
// readFile should have left some extra space for terminators // readFile hopefully have left some extra space for terminators
buffer.append("\0\0", 2); buffer.append("\0\0", 2);
return parse(buffer.data(), buffer.size(), path, dirOf(path), staticEnv); return parse(buffer.data(), buffer.size(), Pos::Origin(path), path.parent(), staticEnv);
} }
Expr * EvalState::parseExprFromString(std::string s_, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv) Expr * EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv)
{ {
auto s = make_ref<std::string>(std::move(s_)); auto s = make_ref<std::string>(std::move(s_));
s->append("\0\0", 2); s->append("\0\0", 2);
@ -724,7 +712,7 @@ Expr * EvalState::parseExprFromString(std::string s_, const Path & basePath, std
} }
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath) Expr * EvalState::parseExprFromString(std::string s, const SourcePath & basePath)
{ {
return parseExprFromString(std::move(s), basePath, staticBaseEnv); return parseExprFromString(std::move(s), basePath, staticBaseEnv);
} }
@ -737,7 +725,7 @@ Expr * EvalState::parseStdin()
// drainFD should have left some extra space for terminators // drainFD should have left some extra space for terminators
buffer.append("\0\0", 2); buffer.append("\0\0", 2);
auto s = make_ref<std::string>(std::move(buffer)); auto s = make_ref<std::string>(std::move(buffer));
return parse(s->data(), s->size(), Pos::Stdin{.source = s}, absPath("."), staticBaseEnv); return parse(s->data(), s->size(), Pos::Stdin{.source = s}, rootPath(CanonPath::fromCwd()), staticBaseEnv);
} }
@ -757,13 +745,13 @@ void EvalState::addToSearchPath(const std::string & s)
} }
Path EvalState::findFile(const std::string_view path) SourcePath EvalState::findFile(const std::string_view path)
{ {
return findFile(searchPath, path); return findFile(searchPath, path);
} }
Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos) SourcePath EvalState::findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos)
{ {
for (auto & i : searchPath) { for (auto & i : searchPath) {
std::string suffix; std::string suffix;
@ -779,11 +767,11 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
auto r = resolveSearchPathElem(i); auto r = resolveSearchPathElem(i);
if (!r.first) continue; if (!r.first) continue;
Path res = r.second + suffix; Path res = r.second + suffix;
if (pathExists(res)) return canonPath(res); if (pathExists(res)) return CanonPath(canonPath(res));
} }
if (hasPrefix(path, "nix/")) if (hasPrefix(path, "nix/"))
return concatStrings(corepkgsPrefix, path.substr(4)); return CanonPath(concatStrings(corepkgsPrefix, path.substr(4)));
debugThrow(ThrownError({ debugThrow(ThrownError({
.msg = hintfmt(evalSettings.pureEval .msg = hintfmt(evalSettings.pureEval

10
src/libexpr/paths.cc Normal file
View file

@ -0,0 +1,10 @@
#include "eval.hh"
namespace nix {
SourcePath EvalState::rootPath(CanonPath path)
{
return std::move(path);
}
}

View file

@ -1,5 +1,6 @@
#include "archive.hh" #include "archive.hh"
#include "derivations.hh" #include "derivations.hh"
#include "downstream-placeholder.hh"
#include "eval-inline.hh" #include "eval-inline.hh"
#include "eval.hh" #include "eval.hh"
#include "globals.hh" #include "globals.hh"
@ -38,17 +39,16 @@ namespace nix {
InvalidPathError::InvalidPathError(const Path & path) : InvalidPathError::InvalidPathError(const Path & path) :
EvalError("path '%s' is not valid", path), path(path) {} EvalError("path '%s' is not valid", path), path(path) {}
StringMap EvalState::realiseContext(const PathSet & context) StringMap EvalState::realiseContext(const NixStringContext & context)
{ {
std::vector<DerivedPath::Built> drvs; std::vector<DerivedPath::Built> drvs;
StringMap res; StringMap res;
for (auto & c_ : context) { for (auto & c : context) {
auto ensureValid = [&](const StorePath & p) { auto ensureValid = [&](const StorePath & p) {
if (!store->isValidPath(p)) if (!store->isValidPath(p))
debugThrowLastTrace(InvalidPathError(store->printStorePath(p))); debugThrowLastTrace(InvalidPathError(store->printStorePath(p)));
}; };
auto c = NixStringContextElem::parse(*store, c_);
std::visit(overloaded { std::visit(overloaded {
[&](const NixStringContextElem::Built & b) { [&](const NixStringContextElem::Built & b) {
drvs.push_back(DerivedPath::Built { drvs.push_back(DerivedPath::Built {
@ -88,7 +88,7 @@ StringMap EvalState::realiseContext(const PathSet & context)
auto outputs = resolveDerivedPath(*store, drv); auto outputs = resolveDerivedPath(*store, drv);
for (auto & [outputName, outputPath] : outputs) { for (auto & [outputName, outputPath] : outputs) {
res.insert_or_assign( res.insert_or_assign(
downstreamPlaceholder(*store, drv.drvPath, outputName), DownstreamPlaceholder::unknownCaOutput(drv.drvPath, outputName).render(),
store->printStorePath(outputPath) store->printStorePath(outputPath)
); );
} }
@ -110,16 +110,16 @@ struct RealisePathFlags {
bool checkForPureEval = true; bool checkForPureEval = true;
}; };
static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const RealisePathFlags flags = {}) static SourcePath realisePath(EvalState & state, const PosIdx pos, Value & v, const RealisePathFlags flags = {})
{ {
PathSet context; NixStringContext context;
auto path = state.coerceToPath(noPos, v, context, "while realising the context of a path"); auto path = state.coerceToPath(noPos, v, context, "while realising the context of a path");
try { try {
StringMap rewrites = state.realiseContext(context); StringMap rewrites = state.realiseContext(context);
auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context); auto realPath = state.rootPath(CanonPath(state.toRealPath(rewriteStrings(path.path.abs(), rewrites), context)));
return flags.checkForPureEval return flags.checkForPureEval
? state.checkSourcePath(realPath) ? state.checkSourcePath(realPath)
@ -130,35 +130,31 @@ static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const Re
} }
} }
/* Add and attribute to the given attribute map from the output name to /**
the output path, or a placeholder. * Add and attribute to the given attribute map from the output name to
* the output path, or a placeholder.
Where possible the path is used, but for floating CA derivations we *
may not know it. For sake of determinism we always assume we don't * Where possible the path is used, but for floating CA derivations we
and instead put in a place holder. In either case, however, the * may not know it. For sake of determinism we always assume we don't
string context will contain the drv path and output name, so * and instead put in a place holder. In either case, however, the
downstream derivations will have the proper dependency, and in * string context will contain the drv path and output name, so
addition, before building, the placeholder will be rewritten to be * downstream derivations will have the proper dependency, and in
the actual path. * addition, before building, the placeholder will be rewritten to be
* the actual path.
The 'drv' and 'drvPath' outputs must correspond. */ *
* The 'drv' and 'drvPath' outputs must correspond.
*/
static void mkOutputString( static void mkOutputString(
EvalState & state, EvalState & state,
BindingsBuilder & attrs, BindingsBuilder & attrs,
const StorePath & drvPath, const StorePath & drvPath,
const BasicDerivation & drv,
const std::pair<std::string, DerivationOutput> & o) const std::pair<std::string, DerivationOutput> & o)
{ {
auto optOutputPath = o.second.path(*state.store, drv.name, o.first); state.mkOutputString(
attrs.alloc(o.first).mkString( attrs.alloc(o.first),
optOutputPath drvPath,
? state.store->printStorePath(*optOutputPath) o.first,
/* Downstream we would substitute this for an actual path once o.second.path(*state.store, Derivation::nameFromPath(drvPath), o.first));
we build the floating CA derivation */
/* FIXME: we need to depend on the basic derivation, not
derivation */
: downstreamPlaceholder(*state.store, drvPath, o.first),
{"!" + o.first + "!" + state.store->printStorePath(drvPath)});
} }
/* Load and evaluate an expression from path specified by the /* Load and evaluate an expression from path specified by the
@ -166,28 +162,30 @@ static void mkOutputString(
static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * vScope, Value & v) static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * vScope, Value & v)
{ {
auto path = realisePath(state, pos, vPath); auto path = realisePath(state, pos, vPath);
auto path2 = path.path.abs();
// FIXME // FIXME
auto isValidDerivationInStore = [&]() -> std::optional<StorePath> { auto isValidDerivationInStore = [&]() -> std::optional<StorePath> {
if (!state.store->isStorePath(path)) if (!state.store->isStorePath(path2))
return std::nullopt; return std::nullopt;
auto storePath = state.store->parseStorePath(path); auto storePath = state.store->parseStorePath(path2);
if (!(state.store->isValidPath(storePath) && isDerivation(path))) if (!(state.store->isValidPath(storePath) && isDerivation(path2)))
return std::nullopt; return std::nullopt;
return storePath; return storePath;
}; };
if (auto optStorePath = isValidDerivationInStore()) { if (auto storePath = isValidDerivationInStore()) {
auto storePath = *optStorePath; Derivation drv = state.store->readDerivation(*storePath);
Derivation drv = state.store->readDerivation(storePath);
auto attrs = state.buildBindings(3 + drv.outputs.size()); auto attrs = state.buildBindings(3 + drv.outputs.size());
attrs.alloc(state.sDrvPath).mkString(path, {"=" + path}); attrs.alloc(state.sDrvPath).mkString(path2, {
NixStringContextElem::DrvDeep { .drvPath = *storePath },
});
attrs.alloc(state.sName).mkString(drv.env["name"]); attrs.alloc(state.sName).mkString(drv.env["name"]);
auto & outputsVal = attrs.alloc(state.sOutputs); auto & outputsVal = attrs.alloc(state.sOutputs);
state.mkList(outputsVal, drv.outputs.size()); state.mkList(outputsVal, drv.outputs.size());
for (const auto & [i, o] : enumerate(drv.outputs)) { for (const auto & [i, o] : enumerate(drv.outputs)) {
mkOutputString(state, attrs, storePath, drv, o); mkOutputString(state, attrs, *storePath, o);
(outputsVal.listElems()[i] = state.allocValue())->mkString(o.first); (outputsVal.listElems()[i] = state.allocValue())->mkString(o.first);
} }
@ -198,7 +196,7 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
state.vImportedDrvToDerivation = allocRootValue(state.allocValue()); state.vImportedDrvToDerivation = allocRootValue(state.allocValue());
state.eval(state.parseExprFromString( state.eval(state.parseExprFromString(
#include "imported-drv-to-derivation.nix.gen.hh" #include "imported-drv-to-derivation.nix.gen.hh"
, "/"), **state.vImportedDrvToDerivation); , CanonPath::root), **state.vImportedDrvToDerivation);
} }
state.forceFunction(**state.vImportedDrvToDerivation, pos, "while evaluating imported-drv-to-derivation.nix.gen.hh"); state.forceFunction(**state.vImportedDrvToDerivation, pos, "while evaluating imported-drv-to-derivation.nix.gen.hh");
@ -206,10 +204,10 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
state.forceAttrs(v, pos, "while calling imported-drv-to-derivation.nix.gen.hh"); state.forceAttrs(v, pos, "while calling imported-drv-to-derivation.nix.gen.hh");
} }
else if (path == corepkgsPrefix + "fetchurl.nix") { else if (path2 == corepkgsPrefix + "fetchurl.nix") {
state.eval(state.parseExprFromString( state.eval(state.parseExprFromString(
#include "fetchurl.nix.gen.hh" #include "fetchurl.nix.gen.hh"
, "/"), v); , CanonPath::root), v);
} }
else { else {
@ -330,7 +328,7 @@ void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Valu
std::string sym(state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.importNative")); std::string sym(state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.importNative"));
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); void *handle = dlopen(path.path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle) if (!handle)
state.debugThrowLastTrace(EvalError("could not open '%1%': %2%", path, dlerror())); state.debugThrowLastTrace(EvalError("could not open '%1%': %2%", path, dlerror()));
@ -358,7 +356,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
auto count = args[0]->listSize(); auto count = args[0]->listSize();
if (count == 0) if (count == 0)
state.error("at least one argument to 'exec' required").atPos(pos).debugThrow<EvalError>(); state.error("at least one argument to 'exec' required").atPos(pos).debugThrow<EvalError>();
PathSet context; NixStringContext context;
auto program = state.coerceToString(pos, *elems[0], context, auto program = state.coerceToString(pos, *elems[0], context,
"while evaluating the first element of the argument passed to builtins.exec", "while evaluating the first element of the argument passed to builtins.exec",
false, false).toOwned(); false, false).toOwned();
@ -378,7 +376,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
auto output = runProgram(program, true, commandArgs); auto output = runProgram(program, true, commandArgs);
Expr * parsed; Expr * parsed;
try { try {
parsed = state.parseExprFromString(std::move(output), "/"); parsed = state.parseExprFromString(std::move(output), state.rootPath(CanonPath::root));
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[pos], "while parsing the output from '%1%'", program); e.addTrace(state.positions[pos], "while parsing the output from '%1%'", program);
throw; throw;
@ -588,7 +586,7 @@ struct CompareValues
case nString: case nString:
return strcmp(v1->string.s, v2->string.s) < 0; return strcmp(v1->string.s, v2->string.s) < 0;
case nPath: case nPath:
return strcmp(v1->path, v2->path) < 0; return strcmp(v1->_path, v2->_path) < 0;
case nList: case nList:
// Lexicographic comparison // Lexicographic comparison
for (size_t i = 0;; i++) { for (size_t i = 0;; i++) {
@ -700,12 +698,14 @@ static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info {
.arity = 1, .arity = 1,
.doc = R"( .doc = R"(
Take an *attrset* with values named `startSet` and `operator` in order to Take an *attrset* with values named `startSet` and `operator` in order to
return a *list of attrsets* by starting with the `startSet`, recursively return a *list of attrsets* by starting with the `startSet` and recursively
applying the `operator` function to each element. The *attrsets* in the applying the `operator` function to each `item`. The *attrsets* in the
`startSet` and produced by the `operator` must each contain value named `startSet` and the *attrsets* produced by `operator` must contain a value
`key` which are comparable to each other. The result is produced by named `key` which is comparable. The result is produced by calling `operator`
repeatedly calling the operator for each element encountered with a for each `item` with a value for `key` that has not been called yet including
unique key, terminating when no new elements are produced. For example, newly produced `item`s. The function terminates when no new `item`s are
produced. The resulting *list of attrsets* contains only *attrsets* with a
unique key. For example,
``` ```
builtins.genericClosure { builtins.genericClosure {
@ -768,7 +768,7 @@ static RegisterPrimOp primop_abort({
)", )",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.abort").toOwned(); "while evaluating the error message passed to builtins.abort").toOwned();
state.debugThrowLastTrace(Abort("evaluation aborted with the following error message: '%1%'", s)); state.debugThrowLastTrace(Abort("evaluation aborted with the following error message: '%1%'", s));
@ -787,7 +787,7 @@ static RegisterPrimOp primop_throw({
)", )",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtin.throw").toOwned(); "while evaluating the error message passed to builtin.throw").toOwned();
state.debugThrowLastTrace(ThrownError(s)); state.debugThrowLastTrace(ThrownError(s));
@ -800,7 +800,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
v = *args[1]; v = *args[1];
} catch (Error & e) { } catch (Error & e) {
PathSet context; NixStringContext context;
auto message = state.coerceToString(pos, *args[0], context, auto message = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.addErrorContext", "while evaluating the error message passed to builtins.addErrorContext",
false, false).toOwned(); false, false).toOwned();
@ -1086,13 +1086,13 @@ drvName, Bindings * attrs, Value & v)
Derivation drv; Derivation drv;
drv.name = drvName; drv.name = drvName;
PathSet context; NixStringContext context;
bool contentAddressed = false; bool contentAddressed = false;
bool isImpure = false; bool isImpure = false;
std::optional<std::string> outputHash; std::optional<std::string> outputHash;
std::string outputHashAlgo; std::string outputHashAlgo;
std::optional<FileIngestionMethod> ingestionMethod; std::optional<ContentAddressMethod> ingestionMethod;
StringSet outputs; StringSet outputs;
outputs.insert("out"); outputs.insert("out");
@ -1105,7 +1105,10 @@ drvName, Bindings * attrs, Value & v)
auto handleHashMode = [&](const std::string_view s) { auto handleHashMode = [&](const std::string_view s) {
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive; if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat; else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else else if (s == "text") {
experimentalFeatureSettings.require(Xp::DynamicDerivations);
ingestionMethod = TextIngestionMethod {};
} else
state.debugThrowLastTrace(EvalError({ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s), .msg = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
.errPos = state.positions[noPos] .errPos = state.positions[noPos]
@ -1149,16 +1152,14 @@ drvName, Bindings * attrs, Value & v)
if (i->value->type() == nNull) continue; if (i->value->type() == nNull) continue;
} }
if (i->name == state.sContentAddressed) { if (i->name == state.sContentAddressed && state.forceBool(*i->value, noPos, context_below)) {
contentAddressed = state.forceBool(*i->value, noPos, context_below); contentAddressed = true;
if (contentAddressed) experimentalFeatureSettings.require(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
} }
else if (i->name == state.sImpure) { else if (i->name == state.sImpure && state.forceBool(*i->value, noPos, context_below)) {
isImpure = state.forceBool(*i->value, noPos, context_below); isImpure = true;
if (isImpure) experimentalFeatureSettings.require(Xp::ImpureDerivations);
experimentalFeatureSettings.require(Xp::ImpureDerivations);
} }
/* The `args' attribute is special: it supplies the /* The `args' attribute is special: it supplies the
@ -1232,8 +1233,7 @@ drvName, Bindings * attrs, Value & v)
/* Everything in the context of the strings in the derivation /* Everything in the context of the strings in the derivation
attributes should be added as dependencies of the resulting attributes should be added as dependencies of the resulting
derivation. */ derivation. */
for (auto & c_ : context) { for (auto & c : context) {
auto c = NixStringContextElem::parse(*state.store, c_);
std::visit(overloaded { std::visit(overloaded {
/* Since this allows the builder to gain access to every /* Since this allows the builder to gain access to every
path in the dependency graph of the derivation (including path in the dependency graph of the derivation (including
@ -1273,11 +1273,16 @@ drvName, Bindings * attrs, Value & v)
})); }));
/* Check whether the derivation name is valid. */ /* Check whether the derivation name is valid. */
if (isDerivation(drvName)) if (isDerivation(drvName) &&
!(ingestionMethod == ContentAddressMethod { TextIngestionMethod { } } &&
outputs.size() == 1 &&
*(outputs.begin()) == "out"))
{
state.debugThrowLastTrace(EvalError({ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("derivation names are not allowed to end in '%s'", drvExtension), .msg = hintfmt("derivation names are allowed to end in '%s' only if they produce a single derivation file", drvExtension),
.errPos = state.positions[noPos] .errPos = state.positions[noPos]
})); }));
}
if (outputHash) { if (outputHash) {
/* Handle fixed-output derivations. /* Handle fixed-output derivations.
@ -1293,21 +1298,15 @@ drvName, Bindings * attrs, Value & v)
auto h = newHashAllowEmpty(*outputHash, parseHashTypeOpt(outputHashAlgo)); auto h = newHashAllowEmpty(*outputHash, parseHashTypeOpt(outputHashAlgo));
auto method = ingestionMethod.value_or(FileIngestionMethod::Flat); auto method = ingestionMethod.value_or(FileIngestionMethod::Flat);
auto outPath = state.store->makeFixedOutputPath(drvName, FixedOutputInfo {
.hash = { DerivationOutput::CAFixed dof {
.method = method, .ca = ContentAddress::fromParts(
.hash = h, std::move(method),
}, std::move(h)),
.references = {}, };
});
drv.env["out"] = state.store->printStorePath(outPath); drv.env["out"] = state.store->printStorePath(dof.path(*state.store, drvName, "out"));
drv.outputs.insert_or_assign("out", drv.outputs.insert_or_assign("out", std::move(dof));
DerivationOutput::CAFixed {
.hash = FixedOutputHash {
.method = method,
.hash = std::move(h),
},
});
} }
else if (contentAddressed || isImpure) { else if (contentAddressed || isImpure) {
@ -1325,13 +1324,13 @@ drvName, Bindings * attrs, Value & v)
if (isImpure) if (isImpure)
drv.outputs.insert_or_assign(i, drv.outputs.insert_or_assign(i,
DerivationOutput::Impure { DerivationOutput::Impure {
.method = method, .method = method.raw,
.hashType = ht, .hashType = ht,
}); });
else else
drv.outputs.insert_or_assign(i, drv.outputs.insert_or_assign(i,
DerivationOutput::CAFloating { DerivationOutput::CAFloating {
.method = method, .method = method.raw,
.hashType = ht, .hashType = ht,
}); });
} }
@ -1392,9 +1391,11 @@ drvName, Bindings * attrs, Value & v)
} }
auto result = state.buildBindings(1 + drv.outputs.size()); auto result = state.buildBindings(1 + drv.outputs.size());
result.alloc(state.sDrvPath).mkString(drvPathS, {"=" + drvPathS}); result.alloc(state.sDrvPath).mkString(drvPathS, {
NixStringContextElem::DrvDeep { .drvPath = drvPath },
});
for (auto & i : drv.outputs) for (auto & i : drv.outputs)
mkOutputString(state, result, drvPath, drv, i); mkOutputString(state, result, drvPath, i);
v.mkAttrs(result); v.mkAttrs(result);
} }
@ -1437,9 +1438,9 @@ static RegisterPrimOp primop_placeholder({
/* Convert the argument to a path. !!! obsolete? */ /* Convert the argument to a path. !!! obsolete? */
static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
Path path = state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.toPath"); auto path = state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.toPath");
v.mkString(canonPath(path), context); v.mkString(path.path.abs(), context);
} }
static RegisterPrimOp primop_toPath({ static RegisterPrimOp primop_toPath({
@ -1468,22 +1469,23 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args,
.errPos = state.positions[pos] .errPos = state.positions[pos]
})); }));
PathSet context; NixStringContext context;
Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.storePath")); auto path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.storePath")).path;
/* Resolve symlinks in path, unless path itself is a symlink /* Resolve symlinks in path, unless path itself is a symlink
directly in the store. The latter condition is necessary so directly in the store. The latter condition is necessary so
e.g. nix-push does the right thing. */ e.g. nix-push does the right thing. */
if (!state.store->isStorePath(path)) path = canonPath(path, true); if (!state.store->isStorePath(path.abs()))
if (!state.store->isInStore(path)) path = CanonPath(canonPath(path.abs(), true));
if (!state.store->isInStore(path.abs()))
state.debugThrowLastTrace(EvalError({ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path), .msg = hintfmt("path '%1%' is not in the Nix store", path),
.errPos = state.positions[pos] .errPos = state.positions[pos]
})); }));
auto path2 = state.store->toStorePath(path).first; auto path2 = state.store->toStorePath(path.abs()).first;
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(path2); state.store->ensurePath(path2);
context.insert(state.store->printStorePath(path2)); context.insert(NixStringContextElem::Opaque { .path = path2 });
v.mkString(path, context); v.mkString(path.abs(), context);
} }
static RegisterPrimOp primop_storePath({ static RegisterPrimOp primop_storePath({
@ -1499,7 +1501,7 @@ static RegisterPrimOp primop_storePath({
causes the path to be *copied* again to the Nix store, resulting causes the path to be *copied* again to the Nix store, resulting
in a new path (e.g. `/nix/store/ld01dnzc-source-source`). in a new path (e.g. `/nix/store/ld01dnzc-source-source`).
This function is not available in pure evaluation mode. Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
)", )",
.fun = prim_storePath, .fun = prim_storePath,
}); });
@ -1514,7 +1516,7 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args,
auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false }); auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
try { try {
v.mkBool(pathExists(state.checkSourcePath(path))); v.mkBool(state.checkSourcePath(path).pathExists());
} catch (SysError & e) { } catch (SysError & e) {
/* Don't give away info from errors while canonicalising /* Don't give away info from errors while canonicalising
path in restricted mode. */ path in restricted mode. */
@ -1538,7 +1540,7 @@ static RegisterPrimOp primop_pathExists({
following the last slash. */ following the last slash. */
static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.baseNameOf", "while evaluating the first argument passed to builtins.baseNameOf",
false, false)), context); false, false)), context);
@ -1560,12 +1562,18 @@ static RegisterPrimOp primop_baseNameOf({
of the argument. */ of the argument. */
static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; state.forceValue(*args[0], pos);
auto path = state.coerceToString(pos, *args[0], context, if (args[0]->type() == nPath) {
"while evaluating the first argument passed to builtins.dirOf", auto path = args[0]->path();
v.mkPath(path.path.isRoot() ? path : path.parent());
} else {
NixStringContext context;
auto path = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to 'builtins.dirOf'",
false, false); false, false);
auto dir = dirOf(*path); auto dir = dirOf(*path);
if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); v.mkString(dir, context);
}
} }
static RegisterPrimOp primop_dirOf({ static RegisterPrimOp primop_dirOf({
@ -1583,13 +1591,13 @@ static RegisterPrimOp primop_dirOf({
static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
auto s = readFile(path); auto s = path.readFile();
if (s.find((char) 0) != std::string::npos) if (s.find((char) 0) != std::string::npos)
state.debugThrowLastTrace(Error("the contents of the file '%1%' cannot be represented as a Nix string", path)); state.debugThrowLastTrace(Error("the contents of the file '%1%' cannot be represented as a Nix string", path));
StorePathSet refs; StorePathSet refs;
if (state.store->isInStore(path)) { if (state.store->isInStore(path.path.abs())) {
try { try {
refs = state.store->queryPathInfo(state.store->toStorePath(path).first)->references; refs = state.store->queryPathInfo(state.store->toStorePath(path.path.abs()).first)->references;
} catch (Error &) { // FIXME: should be InvalidPathError } catch (Error &) { // FIXME: should be InvalidPathError
} }
// Re-scan references to filter down to just the ones that actually occur in the file. // Re-scan references to filter down to just the ones that actually occur in the file.
@ -1597,7 +1605,12 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, V
refsSink << s; refsSink << s;
refs = refsSink.getResultPaths(); refs = refsSink.getResultPaths();
} }
auto context = state.store->printStorePathSet(refs); NixStringContext context;
for (auto && p : std::move(refs)) {
context.insert(NixStringContextElem::Opaque {
.path = std::move((StorePath &&)p),
});
}
v.mkString(s, context); v.mkString(s, context);
} }
@ -1628,7 +1641,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
i = getAttr(state, state.sPath, v2->attrs, "in an element of the __nixPath"); i = getAttr(state, state.sPath, v2->attrs, "in an element of the __nixPath");
PathSet context; NixStringContext context;
auto path = state.coerceToString(pos, *i->value, context, auto path = state.coerceToString(pos, *i->value, context,
"while evaluating the `path` attribute of an element of the list passed to builtins.findFile", "while evaluating the `path` attribute of an element of the list passed to builtins.findFile",
false, false).toOwned(); false, false).toOwned();
@ -1670,7 +1683,7 @@ static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, V
auto path = realisePath(state, pos, *args[1]); auto path = realisePath(state, pos, *args[1]);
v.mkString(hashFile(*ht, path).to_string(Base16, false)); v.mkString(hashString(*ht, path.readFile()).to_string(Base16, false));
} }
static RegisterPrimOp primop_hashFile({ static RegisterPrimOp primop_hashFile({
@ -1684,26 +1697,20 @@ static RegisterPrimOp primop_hashFile({
.fun = prim_hashFile, .fun = prim_hashFile,
}); });
static std::string_view fileTypeToString(InputAccessor::Type type)
/* Stringize a directory entry enum. Used by `readFileType' and `readDir'. */
static const char * dirEntTypeToString(unsigned char dtType)
{ {
/* Enum DT_(DIR|LNK|REG|UNKNOWN) */ return
switch(dtType) { type == InputAccessor::Type::tRegular ? "regular" :
case DT_REG: return "regular"; break; type == InputAccessor::Type::tDirectory ? "directory" :
case DT_DIR: return "directory"; break; type == InputAccessor::Type::tSymlink ? "symlink" :
case DT_LNK: return "symlink"; break; "unknown";
default: return "unknown"; break;
}
return "unknown"; /* Unreachable */
} }
static void prim_readFileType(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_readFileType(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
/* Retrieve the directory entry type and stringize it. */ /* Retrieve the directory entry type and stringize it. */
v.mkString(dirEntTypeToString(getFileType(path))); v.mkString(fileTypeToString(path.lstat().type));
} }
static RegisterPrimOp primop_readFileType({ static RegisterPrimOp primop_readFileType({
@ -1724,8 +1731,7 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Va
// Retrieve directory entries for all nodes in a directory. // Retrieve directory entries for all nodes in a directory.
// This is similar to `getFileType` but is optimized to reduce system calls // This is similar to `getFileType` but is optimized to reduce system calls
// on many systems. // on many systems.
DirEntries entries = readDirectory(path); auto entries = path.readDirectory();
auto attrs = state.buildBindings(entries.size()); auto attrs = state.buildBindings(entries.size());
// If we hit unknown directory entry types we may need to fallback to // If we hit unknown directory entry types we may need to fallback to
@ -1734,22 +1740,21 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Va
// `builtins.readFileType` application. // `builtins.readFileType` application.
Value * readFileType = nullptr; Value * readFileType = nullptr;
for (auto & ent : entries) { for (auto & [name, type] : entries) {
auto & attr = attrs.alloc(ent.name); auto & attr = attrs.alloc(name);
if (ent.type == DT_UNKNOWN) { if (!type) {
// Some filesystems or operating systems may not be able to return // Some filesystems or operating systems may not be able to return
// detailed node info quickly in this case we produce a thunk to // detailed node info quickly in this case we produce a thunk to
// query the file type lazily. // query the file type lazily.
auto epath = state.allocValue(); auto epath = state.allocValue();
Path path2 = path + "/" + ent.name; epath->mkPath(path + name);
epath->mkString(path2);
if (!readFileType) if (!readFileType)
readFileType = &state.getBuiltin("readFileType"); readFileType = &state.getBuiltin("readFileType");
attr.mkApp(readFileType, epath); attr.mkApp(readFileType, epath);
} else { } else {
// This branch of the conditional is much more likely. // This branch of the conditional is much more likely.
// Here we just stringize the directory entry type. // Here we just stringize the directory entry type.
attr.mkString(dirEntTypeToString(ent.type)); attr.mkString(fileTypeToString(*type));
} }
} }
@ -1787,7 +1792,7 @@ static RegisterPrimOp primop_readDir({
static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
std::ostringstream out; std::ostringstream out;
PathSet context; NixStringContext context;
printValueAsXML(state, true, false, *args[0], out, context, pos); printValueAsXML(state, true, false, *args[0], out, context, pos);
v.mkString(out.str(), context); v.mkString(out.str(), context);
} }
@ -1895,7 +1900,7 @@ static RegisterPrimOp primop_toXML({
static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
std::ostringstream out; std::ostringstream out;
PathSet context; NixStringContext context;
printValueAsJSON(state, true, *args[0], pos, out, context); printValueAsJSON(state, true, *args[0], pos, out, context);
v.mkString(out.str(), context); v.mkString(out.str(), context);
} }
@ -1945,22 +1950,23 @@ static RegisterPrimOp primop_fromJSON({
as an input by derivations. */ as an input by derivations. */
static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
std::string name(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.toFile")); std::string name(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.toFile"));
std::string contents(state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.toFile")); std::string contents(state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.toFile"));
StorePathSet refs; StorePathSet refs;
for (auto path : context) { for (auto c : context) {
if (path.at(0) != '/') if (auto p = std::get_if<NixStringContextElem::Opaque>(&c))
refs.insert(p->path);
else
state.debugThrowLastTrace(EvalError({ state.debugThrowLastTrace(EvalError({
.msg = hintfmt( .msg = hintfmt(
"in 'toFile': the file named '%1%' must not contain a reference " "in 'toFile': the file named '%1%' must not contain a reference "
"to a derivation but contains (%2%)", "to a derivation but contains (%2%)",
name, path), name, c.to_string()),
.errPos = state.positions[pos] .errPos = state.positions[pos]
})); }));
refs.insert(state.store->parseStorePath(path));
} }
auto storePath = settings.readOnlyMode auto storePath = settings.readOnlyMode
@ -2055,13 +2061,13 @@ static RegisterPrimOp primop_toFile({
static void addPath( static void addPath(
EvalState & state, EvalState & state,
const PosIdx pos, const PosIdx pos,
const std::string & name, std::string_view name,
Path path, Path path,
Value * filterFun, Value * filterFun,
FileIngestionMethod method, FileIngestionMethod method,
const std::optional<Hash> expectedHash, const std::optional<Hash> expectedHash,
Value & v, Value & v,
const PathSet & context) const NixStringContext & context)
{ {
try { try {
// FIXME: handle CA derivation outputs (where path needs to // FIXME: handle CA derivation outputs (where path needs to
@ -2083,7 +2089,7 @@ static void addPath(
path = evalSettings.pureEval && expectedHash path = evalSettings.pureEval && expectedHash
? path ? path
: state.checkSourcePath(path); : state.checkSourcePath(CanonPath(path)).path.abs();
PathFilter filter = filterFun ? ([&](const Path & path) { PathFilter filter = filterFun ? ([&](const Path & path) {
auto st = lstat(path); auto st = lstat(path);
@ -2135,10 +2141,11 @@ static void addPath(
static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
Path path = state.coerceToPath(pos, *args[1], context, "while evaluating the second argument (the path to filter) passed to builtins.filterSource"); auto path = state.coerceToPath(pos, *args[1], context,
"while evaluating the second argument (the path to filter) passed to builtins.filterSource");
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource"); state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource");
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); addPath(state, pos, path.baseName(), path.path.abs(), args[0], FileIngestionMethod::Recursive, std::nullopt, v, context);
} }
static RegisterPrimOp primop_filterSource({ static RegisterPrimOp primop_filterSource({
@ -2198,18 +2205,19 @@ static RegisterPrimOp primop_filterSource({
static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.path"); std::optional<SourcePath> path;
Path path;
std::string name; std::string name;
Value * filterFun = nullptr; Value * filterFun = nullptr;
auto method = FileIngestionMethod::Recursive; auto method = FileIngestionMethod::Recursive;
std::optional<Hash> expectedHash; std::optional<Hash> expectedHash;
PathSet context; NixStringContext context;
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to 'builtins.path'");
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
auto n = state.symbols[attr.name]; auto n = state.symbols[attr.name];
if (n == "path") if (n == "path")
path = state.coerceToPath(attr.pos, *attr.value, context, "while evaluating the `path` attribute passed to builtins.path"); path.emplace(state.coerceToPath(attr.pos, *attr.value, context, "while evaluating the 'path' attribute passed to 'builtins.path'"));
else if (attr.name == state.sName) else if (attr.name == state.sName)
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.path"); name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.path");
else if (n == "filter") else if (n == "filter")
@ -2224,15 +2232,15 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
.errPos = state.positions[attr.pos] .errPos = state.positions[attr.pos]
})); }));
} }
if (path.empty()) if (!path)
state.debugThrowLastTrace(EvalError({ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("missing required 'path' attribute in the first argument to builtins.path"), .msg = hintfmt("missing required 'path' attribute in the first argument to builtins.path"),
.errPos = state.positions[pos] .errPos = state.positions[pos]
})); }));
if (name.empty()) if (name.empty())
name = baseNameOf(path); name = path->baseName();
addPath(state, pos, name, path, filterFun, method, expectedHash, v, context); addPath(state, pos, name, path->path.abs(), filterFun, method, expectedHash, v, context);
} }
static RegisterPrimOp primop_path({ static RegisterPrimOp primop_path({
@ -3538,7 +3546,7 @@ static RegisterPrimOp primop_lessThan({
`"/nix/store/whatever..."'. */ `"/nix/store/whatever..."'. */
static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.toString", "while evaluating the first argument passed to builtins.toString",
true, false); true, false);
@ -3577,7 +3585,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args,
{ {
int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring"); int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring");
int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring"); int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring"); auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
if (start < 0) if (start < 0)
@ -3611,7 +3619,7 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.stringLength"); auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.stringLength");
v.mkInt(s->size()); v.mkInt(s->size());
} }
@ -3637,7 +3645,7 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args,
.errPos = state.positions[pos] .errPos = state.positions[pos]
})); }));
PathSet context; // discarded NixStringContext context; // discarded
auto s = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.hashString"); auto s = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.hashString");
v.mkString(hashString(*ht, s).to_string(Base16, false)); v.mkString(hashString(*ht, s).to_string(Base16, false));
@ -3683,7 +3691,7 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v)
auto regex = state.regexCache->get(re); auto regex = state.regexCache->get(re);
PathSet context; NixStringContext context;
const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.match"); const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.match");
std::cmatch match; std::cmatch match;
@ -3763,7 +3771,7 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
auto regex = state.regexCache->get(re); auto regex = state.regexCache->get(re);
PathSet context; NixStringContext context;
const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.split"); const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.split");
auto begin = std::cregex_iterator(str.begin(), str.end(), regex); auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
@ -3860,7 +3868,7 @@ static RegisterPrimOp primop_split({
static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto sep = state.forceString(*args[0], context, pos, "while evaluating the first argument (the separator string) passed to builtins.concatStringsSep"); auto sep = state.forceString(*args[0], context, pos, "while evaluating the first argument (the separator string) passed to builtins.concatStringsSep");
state.forceList(*args[1], pos, "while evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep"); state.forceList(*args[1], pos, "while evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep");
@ -3900,15 +3908,10 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
for (auto elem : args[0]->listItems()) for (auto elem : args[0]->listItems())
from.emplace_back(state.forceString(*elem, pos, "while evaluating one of the strings to replace passed to builtins.replaceStrings")); from.emplace_back(state.forceString(*elem, pos, "while evaluating one of the strings to replace passed to builtins.replaceStrings"));
std::vector<std::pair<std::string, PathSet>> to; std::unordered_map<size_t, std::string> cache;
to.reserve(args[1]->listSize()); auto to = args[1]->listItems();
for (auto elem : args[1]->listItems()) {
PathSet ctx;
auto s = state.forceString(*elem, ctx, pos, "while evaluating one of the replacement strings passed to builtins.replaceStrings");
to.emplace_back(s, std::move(ctx));
}
PathSet context; NixStringContext context;
auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.replaceStrings"); auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.replaceStrings");
std::string res; std::string res;
@ -3917,10 +3920,19 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
bool found = false; bool found = false;
auto i = from.begin(); auto i = from.begin();
auto j = to.begin(); auto j = to.begin();
for (; i != from.end(); ++i, ++j) size_t j_index = 0;
for (; i != from.end(); ++i, ++j, ++j_index)
if (s.compare(p, i->size(), *i) == 0) { if (s.compare(p, i->size(), *i) == 0) {
found = true; found = true;
res += j->first; auto v = cache.find(j_index);
if (v == cache.end()) {
NixStringContext ctx;
auto ts = state.forceString(**j, ctx, pos, "while evaluating one of the replacement strings passed to builtins.replaceStrings");
v = (cache.emplace(j_index, ts)).first;
for (auto& path : ctx)
context.insert(path);
}
res += v->second;
if (i->empty()) { if (i->empty()) {
if (p < s.size()) if (p < s.size())
res += s[p]; res += s[p];
@ -3928,9 +3940,6 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
} else { } else {
p += i->size(); p += i->size();
} }
for (auto& path : j->second)
context.insert(path);
j->second.clear();
break; break;
} }
if (!found) { if (!found) {
@ -3948,7 +3957,11 @@ static RegisterPrimOp primop_replaceStrings({
.args = {"from", "to", "s"}, .args = {"from", "to", "s"},
.doc = R"( .doc = R"(
Given string *s*, replace every occurrence of the strings in *from* Given string *s*, replace every occurrence of the strings in *from*
with the corresponding string in *to*. For example, with the corresponding string in *to*.
The argument *to* is lazy, that is, it is only evaluated when its corresponding pattern in *from* is matched in the string *s*
Example:
```nix ```nix
builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar" builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar"
@ -4150,7 +4163,6 @@ void EvalState::createBaseEnv()
/* Add a wrapper around the derivation primop that computes the /* Add a wrapper around the derivation primop that computes the
`drvPath' and `outPath' attributes lazily. */ `drvPath' and `outPath' attributes lazily. */
sDerivationNix = symbols.create(derivationNixPath);
auto vDerivation = allocValue(); auto vDerivation = allocValue();
addConstant("derivation", vDerivation); addConstant("derivation", vDerivation);
@ -4167,7 +4179,7 @@ void EvalState::createBaseEnv()
// the parser needs two NUL bytes as terminators; one of them // the parser needs two NUL bytes as terminators; one of them
// is implied by being a C string. // is implied by being a C string.
"\0"; "\0";
eval(parse(code, sizeof(code), derivationNixPath, "/", staticBaseEnv), *vDerivation); eval(parse(code, sizeof(code), derivationInternal, {CanonPath::root}, staticBaseEnv), *vDerivation);
} }

View file

@ -7,7 +7,7 @@ namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext"); auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
v.mkString(*s); v.mkString(*s);
} }
@ -17,7 +17,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext"); state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
v.mkBool(!context.empty()); v.mkBool(!context.empty());
} }
@ -33,17 +33,18 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
drv.inputDrvs. */ drv.inputDrvs. */
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
PathSet context2; NixStringContext context2;
for (auto && p : context) { for (auto && c : context) {
auto c = NixStringContextElem::parse(*state.store, p);
if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c)) { if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c)) {
context2.emplace(state.store->printStorePath(ptr->drvPath)); context2.emplace(NixStringContextElem::Opaque {
.path = ptr->drvPath
});
} else { } else {
/* Can reuse original item */ /* Can reuse original item */
context2.emplace(std::move(p)); context2.emplace(std::move(c));
} }
} }
@ -79,22 +80,21 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
bool allOutputs = false; bool allOutputs = false;
Strings outputs; Strings outputs;
}; };
PathSet context; NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext"); state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
auto contextInfos = std::map<StorePath, ContextInfo>(); auto contextInfos = std::map<StorePath, ContextInfo>();
for (const auto & p : context) { for (auto && i : context) {
NixStringContextElem ctx = NixStringContextElem::parse(*state.store, p);
std::visit(overloaded { std::visit(overloaded {
[&](NixStringContextElem::DrvDeep & d) { [&](NixStringContextElem::DrvDeep && d) {
contextInfos[d.drvPath].allOutputs = true; contextInfos[std::move(d.drvPath)].allOutputs = true;
}, },
[&](NixStringContextElem::Built & b) { [&](NixStringContextElem::Built && b) {
contextInfos[b.drvPath].outputs.emplace_back(std::move(b.output)); contextInfos[std::move(b.drvPath)].outputs.emplace_back(std::move(b.output));
}, },
[&](NixStringContextElem::Opaque & o) { [&](NixStringContextElem::Opaque && o) {
contextInfos[o.path].path = true; contextInfos[std::move(o.path)].path = true;
}, },
}, ctx.raw()); }, ((NixStringContextElem &&) i).raw());
} }
auto attrs = state.buildBindings(contextInfos.size()); auto attrs = state.buildBindings(contextInfos.size());
@ -129,7 +129,7 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
*/ */
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext"); auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
@ -143,13 +143,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
.msg = hintfmt("context key '%s' is not a store path", name), .msg = hintfmt("context key '%s' is not a store path", name),
.errPos = state.positions[i.pos] .errPos = state.positions[i.pos]
}); });
auto namePath = state.store->parseStorePath(name);
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(name)); state.store->ensurePath(namePath);
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context"); state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
auto iter = i.value->attrs->find(sPath); auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context")) if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context"))
context.emplace(name); context.emplace(NixStringContextElem::Opaque {
.path = namePath,
});
} }
iter = i.value->attrs->find(sAllOutputs); iter = i.value->attrs->find(sAllOutputs);
@ -161,7 +164,9 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
.errPos = state.positions[i.pos] .errPos = state.positions[i.pos]
}); });
} }
context.insert(concatStrings("=", name)); context.emplace(NixStringContextElem::DrvDeep {
.drvPath = namePath,
});
} }
} }
@ -176,7 +181,10 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
} }
for (auto elem : iter->value->listItems()) { for (auto elem : iter->value->listItems()) {
auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context"); auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context");
context.insert(concatStrings("!", outputName, "!", name)); context.emplace(NixStringContextElem::Built {
.drvPath = namePath,
.output = std::string { outputName },
});
} }
} }
} }

View file

@ -18,7 +18,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
const auto & attrName = state.symbols[attr.name]; const auto & attrName = state.symbols[attr.name];
if (attrName == "fromPath") { if (attrName == "fromPath") {
PathSet context; NixStringContext context;
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context, fromPath = state.coerceToStorePath(attr.pos, *attr.value, context,
"while evaluating the 'fromPath' attribute passed to builtins.fetchClosure"); "while evaluating the 'fromPath' attribute passed to builtins.fetchClosure");
} }
@ -27,7 +27,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
toCA = true; toCA = true;
if (attr.value->type() != nString || attr.value->string.s != std::string("")) { if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
PathSet context; NixStringContext context;
toPath = state.coerceToStorePath(attr.pos, *attr.value, context, toPath = state.coerceToStorePath(attr.pos, *attr.value, context,
"while evaluating the 'toPath' attribute passed to builtins.fetchClosure"); "while evaluating the 'toPath' attribute passed to builtins.fetchClosure");
} }
@ -114,8 +114,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
}); });
} }
auto toPathS = state.store->printStorePath(*toPath); state.mkStorePathString(*toPath, v);
v.mkString(toPathS, {toPathS});
} }
static RegisterPrimOp primop_fetchClosure({ static RegisterPrimOp primop_fetchClosure({

View file

@ -13,7 +13,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
std::optional<Hash> rev; std::optional<Hash> rev;
std::optional<std::string> ref; std::optional<std::string> ref;
std::string_view name = "source"; std::string_view name = "source";
PathSet context; NixStringContext context;
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
@ -73,8 +73,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
auto [tree, input2] = input.fetch(state.store); auto [tree, input2] = input.fetch(state.store);
auto attrs2 = state.buildBindings(8); auto attrs2 = state.buildBindings(8);
auto storePath = state.store->printStorePath(tree.storePath); state.mkStorePathString(tree.storePath, attrs2.alloc(state.sOutPath));
attrs2.alloc(state.sOutPath).mkString(storePath, {storePath});
if (input2.getRef()) if (input2.getRef())
attrs2.alloc("branch").mkString(*input2.getRef()); attrs2.alloc("branch").mkString(*input2.getRef());
// Backward compatibility: set 'rev' to // Backward compatibility: set 'rev' to

View file

@ -24,9 +24,8 @@ void emitTreeAttrs(
auto attrs = state.buildBindings(8); auto attrs = state.buildBindings(8);
auto storePath = state.store->printStorePath(tree.storePath);
attrs.alloc(state.sOutPath).mkString(storePath, {storePath}); state.mkStorePathString(tree.storePath, attrs.alloc(state.sOutPath));
// FIXME: support arbitrary input attributes. // FIXME: support arbitrary input attributes.
@ -107,7 +106,7 @@ static void fetchTree(
const FetchTreeParams & params = FetchTreeParams{} const FetchTreeParams & params = FetchTreeParams{}
) { ) {
fetchers::Input input; fetchers::Input input;
PathSet context; NixStringContext context;
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
@ -287,9 +286,9 @@ static RegisterPrimOp primop_fetchurl({
.name = "__fetchurl", .name = "__fetchurl",
.args = {"url"}, .args = {"url"},
.doc = R"( .doc = R"(
Download the specified URL and return the path of the downloaded Download the specified URL and return the path of the downloaded file.
file. This function is not available if [restricted evaluation
mode](../command-ref/conf-file.md) is enabled. Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)", )",
.fun = prim_fetchurl, .fun = prim_fetchurl,
}); });
@ -339,8 +338,7 @@ static RegisterPrimOp primop_fetchTarball({
stdenv.mkDerivation { } stdenv.mkDerivation { }
``` ```
This function is not available if [restricted evaluation Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
mode](../command-ref/conf-file.md) is enabled.
)", )",
.fun = prim_fetchTarball, .fun = prim_fetchTarball,
}); });
@ -471,14 +469,9 @@ static RegisterPrimOp primop_fetchGit({
} }
``` ```
> **Note** Nix will refetch the branch according to the [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl) setting.
>
> Nix will refetch the branch in accordance with
> the option `tarball-ttl`.
> **Note** This behavior is disabled in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
>
> This behavior is disabled in *Pure evaluation mode*.
- To fetch the content of a checked-out work directory: - To fetch the content of a checked-out work directory:

View file

@ -3,6 +3,8 @@
#include "../../toml11/toml.hpp" #include "../../toml11/toml.hpp"
#include <sstream>
namespace nix { namespace nix {
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val) static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
@ -58,8 +60,18 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
case toml::value_t::offset_datetime: case toml::value_t::offset_datetime:
case toml::value_t::local_date: case toml::value_t::local_date:
case toml::value_t::local_time: case toml::value_t::local_time:
// We fail since Nix doesn't have date and time types {
throw std::runtime_error("Dates and times are not supported"); if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
auto attrs = state.buildBindings(2);
attrs.alloc("_type").mkString("timestamp");
std::ostringstream s;
s << t;
attrs.alloc("value").mkString(s.str());
v.mkAttrs(attrs);
} else {
throw std::runtime_error("Dates and times are not supported");
}
}
break;; break;;
case toml::value_t::empty: case toml::value_t::empty:
v.mkNull(); v.mkNull();

View file

@ -1,4 +1,5 @@
#include "print.hh" #include "print.hh"
#include <unordered_set>
namespace nix { namespace nix {
@ -25,11 +26,26 @@ printLiteralBool(std::ostream & str, bool boolean)
return str; return str;
} }
// Returns `true' is a string is a reserved keyword which requires quotation
// when printing attribute set field names.
//
// This list should generally be kept in sync with `./lexer.l'.
// You can test if a keyword needs to be added by running:
// $ nix eval --expr '{ <KEYWORD> = 1; }'
// For example `or' doesn't need to be quoted.
bool isReservedKeyword(const std::string_view str)
{
static const std::unordered_set<std::string_view> reservedKeywords = {
"if", "then", "else", "assert", "with", "let", "in", "rec", "inherit"
};
return reservedKeywords.contains(str);
}
std::ostream & std::ostream &
printIdentifier(std::ostream & str, std::string_view s) { printIdentifier(std::ostream & str, std::string_view s) {
if (s.empty()) if (s.empty())
str << "\"\""; str << "\"\"";
else if (s == "if") // FIXME: handle other keywords else if (isReservedKeyword(s))
str << '"' << s << '"'; str << '"' << s << '"';
else { else {
char c = s[0]; char c = s[0];
@ -50,10 +66,10 @@ printIdentifier(std::ostream & str, std::string_view s) {
return str; return str;
} }
// FIXME: keywords
static bool isVarName(std::string_view s) static bool isVarName(std::string_view s)
{ {
if (s.size() == 0) return false; if (s.size() == 0) return false;
if (isReservedKeyword(s)) return false;
char c = s[0]; char c = s[0];
if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false; if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false;
for (auto & i : s) for (auto & i : s)

View file

@ -35,6 +35,12 @@ namespace nix {
*/ */
std::ostream & printAttributeName(std::ostream & o, std::string_view s); std::ostream & printAttributeName(std::ostream & o, std::string_view s);
/**
* Returns `true' is a string is a reserved keyword which requires quotation
* when printing attribute set field names.
*/
bool isReservedKeyword(const std::string_view str);
/** /**
* Print a string as an identifier in the Nix expression language syntax. * Print a string as an identifier in the Nix expression language syntax.
* *

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