forked from lix-project/lix
Merge branch 'master' into angerman/mac-fix-recursive-nix
This commit is contained in:
commit
381a32981b
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
|
@ -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 }}
|
||||||
|
|
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
|
@ -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
3
.gitignore
vendored
|
@ -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/
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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,$@,$@)
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 can’t or don’t want to configure `root` to be able to
|
> If you can’t or don’t 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
|
||||||
|
|
|
@ -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'...
|
||||||
|
|
|
@ -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
|
26
doc/manual/src/command-ref/files/channels.md
Normal file
26
doc/manual/src/command-ref/files/channels.md
Normal 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
|
52
doc/manual/src/command-ref/files/default-nix-expression.md
Normal file
52
doc/manual/src/command-ref/files/default-nix-expression.md
Normal 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
|
45
doc/manual/src/command-ref/files/manifest.json.md
Normal file
45
doc/manual/src/command-ref/files/manifest.json.md
Normal 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.
|
128
doc/manual/src/command-ref/files/manifest.nix.md
Normal file
128
doc/manual/src/command-ref/files/manifest.nix.md
Normal 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.
|
74
doc/manual/src/command-ref/files/profiles.md
Normal file
74
doc/manual/src/command-ref/files/profiles.md
Normal 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
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
@ -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 channel’s 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 channel’s “entry point”.
|
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
@ -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.
|
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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 what’s available:
|
To compare installed versions to what’s 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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
```
|
```
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
@ -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)
|
||||||
```
|
```
|
||||||
|
|
|
@ -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'
|
||||||
```
|
```
|
||||||
|
|
|
@ -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 doesn’t
|
||||||
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" />
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -23,7 +23,7 @@ produce the same NAR archive. For instance, directory entries are
|
||||||
always sorted so that the actual on-disk order doesn’t influence the
|
always sorted so that the actual on-disk order doesn’t 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 Nix’s database (see `nix-store -q
|
the hashes of store paths stored in Nix’s 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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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*\
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
148
doc/manual/src/installation/uninstall.md
Normal file
148
doc/manual/src/installation/uninstall.md
Normal 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.
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
You’re then dropped into a shell where you can edit, build and test
|
You’re then dropped into a shell where you can edit, build and test
|
||||||
|
|
|
@ -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
|
Contains all the [built-in functions](./builtins.md) and values, in order to avoid polluting the global scope.
|
||||||
if builtins ? getEnv then builtins.getEnv "PATH" else ""
|
|
||||||
```
|
|
||||||
|
|
||||||
This allows a Nix expression to fall back gracefully on older Nix
|
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:
|
||||||
installations that don’t have the desired built-in function.
|
|
||||||
|
|
||||||
- [`builtins.currentSystem`]{#builtins-currentSystem}\
|
```nix
|
||||||
The built-in value `currentSystem` evaluates to the Nix platform
|
if builtins ? getEnv then builtins.getEnv "PATH" else ""
|
||||||
identifier for the Nix installation on which the expression is being
|
```
|
||||||
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.
|
|
||||||
|
- [`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).
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 doesn’t exist, return *value* if provided, otherwise abort evaluation.
|
If the attribute doesn’t 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
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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 Nix’s 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 it’s useful to be able to ask what `nix-env` would do, without
|
Sometimes it’s 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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -6,7 +6,7 @@ automatically fetching any store paths in Firefox’s 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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
8
doc/manual/src/release-notes/rl-2.16.md
Normal file
8
doc/manual/src/release-notes/rl-2.16.md
Normal 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`.
|
14
docker.nix
14
docker.nix
|
@ -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 {
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
5
mk/cxx-big-literal.mk
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
%.gen.hh: %
|
||||||
|
@echo 'R"foo(' >> $@.tmp
|
||||||
|
$(trace-gen) cat $< >> $@.tmp
|
||||||
|
@echo ')foo"' >> $@.tmp
|
||||||
|
@mv $@.tmp $@
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 don’t do
|
||||||
|
// that for the derivations whose `inputDrvs` is empty
|
||||||
|
// because:
|
||||||
|
//
|
||||||
|
// 1. It’s 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 don’t do that for the derivations whose `inputDrvs` is empty
|
|
||||||
// because
|
|
||||||
// 1. It’s 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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
10
src/libexpr/paths.cc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include "eval.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
SourcePath EvalState::rootPath(CanonPath path)
|
||||||
|
{
|
||||||
|
return std::move(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
Loading…
Reference in a new issue