Merge remote-tracking branch 'origin/master' into best-effort-supplementary-groups

This commit is contained in:
Ben Radford 2023-07-11 09:38:34 +01:00
commit 25b20b4ad2
No known key found for this signature in database
GPG key ID: 9DF5D4640AB888D5
236 changed files with 5449 additions and 2033 deletions

View file

@ -11,6 +11,10 @@ assignees: ''
<!-- describe your problem --> <!-- describe your problem -->
## Proposal
<!-- propose a solution -->
## Checklist ## Checklist
<!-- make sure this issue is not redundant or obsolete --> <!-- make sure this issue is not redundant or obsolete -->
@ -22,10 +26,6 @@ assignees: ''
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/src [source]: https://github.com/NixOS/nix/tree/master/doc/manual/src
[open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation [open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation
## Proposal
<!-- propose a solution -->
## Priorities ## Priorities
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

2
.github/labeler.yml vendored
View file

@ -16,7 +16,7 @@
"new-cli": "new-cli":
- src/nix/**/* - src/nix/**/*
"tests": "with-tests":
# Unit tests # Unit tests
- src/*/tests/**/* - src/*/tests/**/*
# Functional and integration tests # Functional and integration tests

View file

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

View file

@ -11,6 +11,7 @@ jobs:
tests: tests:
needs: [check_secrets] needs: [check_secrets]
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ubuntu-latest, macos-latest] os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -19,7 +20,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@v22
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 +62,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@v22
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
@ -73,13 +77,14 @@ jobs:
needs: [installer, check_secrets] needs: [installer, check_secrets]
if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true' if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ubuntu-latest, macos-latest] os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
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@v22
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 +111,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@v22
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

4
.gitignore vendored
View file

@ -18,7 +18,7 @@ perl/Makefile.config
/doc/manual/generated/* /doc/manual/generated/*
/doc/manual/nix.json /doc/manual/nix.json
/doc/manual/conf-file.json /doc/manual/conf-file.json
/doc/manual/builtins.json /doc/manual/language.json
/doc/manual/xp-features.json /doc/manual/xp-features.json
/doc/manual/src/SUMMARY.md /doc/manual/src/SUMMARY.md
/doc/manual/src/command-ref/new-cli /doc/manual/src/command-ref/new-cli
@ -26,6 +26,7 @@ perl/Makefile.config
/doc/manual/src/command-ref/experimental-features-shortlist.md /doc/manual/src/command-ref/experimental-features-shortlist.md
/doc/manual/src/contributing/experimental-feature-descriptions.md /doc/manual/src/contributing/experimental-feature-descriptions.md
/doc/manual/src/language/builtins.md /doc/manual/src/language/builtins.md
/doc/manual/src/language/builtin-constants.md
# /scripts/ # /scripts/
/scripts/nix-profile.sh /scripts/nix-profile.sh
@ -89,6 +90,7 @@ perl/Makefile.config
/tests/ca/config.nix /tests/ca/config.nix
/tests/dyn-drv/config.nix /tests/dyn-drv/config.nix
/tests/repl-result-out /tests/repl-result-out
/tests/test-libstoreconsumer/test-libstoreconsumer
# /tests/lang/ # /tests/lang/
/tests/lang/*.out /tests/lang/*.out

View file

@ -1 +1 @@
2.16.0 2.17.0

View file

@ -5,7 +5,6 @@ We appreciate your support.
Reading and following these guidelines will help us make the contribution process easy and effective for everyone involved. Reading and following these guidelines will help us make the contribution process easy and effective for everyone involved.
## Report a bug ## Report a bug
1. Check on the [GitHub issue tracker](https://github.com/NixOS/nix/issues) if your bug was already reported. 1. Check on the [GitHub issue tracker](https://github.com/NixOS/nix/issues) if your bug was already reported.
@ -30,6 +29,8 @@ 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.
Issues labeled ["good first issue"](https://github.com/NixOS/nix/labels/good-first-issue) should be relatively easy to fix and are likely to get merged quickly.
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. Pull requests addressing issues labeled ["idea approved"](https://github.com/NixOS/nix/labels/idea%20approved) are especially welcomed by maintainers and will receive prioritised review.
3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests. 3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests.

View file

@ -27,6 +27,7 @@ makefiles += \
src/libstore/tests/local.mk \ src/libstore/tests/local.mk \
src/libexpr/tests/local.mk \ src/libexpr/tests/local.mk \
tests/local.mk \ tests/local.mk \
tests/test-libstoreconsumer/local.mk \
tests/plugins/local.mk tests/plugins/local.mk
else else
makefiles += \ makefiles += \

View file

@ -0,0 +1,29 @@
let
inherit (builtins) concatStringsSep attrValues mapAttrs;
inherit (import ./utils.nix) optionalString squash;
in
builtinsInfo:
let
showBuiltin = name: { doc, type, impure-only }:
let
type' = optionalString (type != null) " (${type})";
impureNotice = optionalString impure-only ''
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
'';
in
squash ''
<dt id="builtin-constants-${name}">
<a href="#builtin-constants-${name}"><code>${name}</code>${type'}</a>
</dt>
<dd>
${doc}
${impureNotice}
</dd>
'';
in
concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo))

View file

@ -1,24 +1,28 @@
let let
inherit (builtins) concatStringsSep attrNames; inherit (builtins) concatStringsSep attrValues mapAttrs;
inherit (import ./utils.nix) optionalString squash;
in in
builtinsInfo: builtinsInfo:
let let
showBuiltin = name: showBuiltin = name: { doc, args, arity, experimental-feature }:
let let
inherit (builtinsInfo.${name}) doc args; experimentalNotice = optionalString (experimental-feature != null) ''
This function is only available if the [${experimental-feature}](@docroot@/contributing/experimental-features.md#xp-feature-${experimental-feature}) experimental feature is enabled.
'';
in in
'' squash ''
<dt id="builtins-${name}"> <dt id="builtins-${name}">
<a href="#builtins-${name}"><code>${name} ${listArgs args}</code></a> <a href="#builtins-${name}"><code>${name} ${listArgs args}</code></a>
</dt> </dt>
<dd> <dd>
${doc} ${doc}
${experimentalNotice}
</dd> </dd>
''; '';
listArgs = args: concatStringsSep " " (map (s: "<var>${s}</var>") args); listArgs = args: concatStringsSep " " (map (s: "<var>${s}</var>") args);
in in
concatStringsSep "\n" (map showBuiltin (attrNames builtinsInfo)) concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo))

View file

@ -128,14 +128,20 @@ $(d)/xp-features.json: $(bindir)/nix
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp $(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix $(d)/src/language/builtins.md: $(d)/language.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp @cat doc/manual/src/language/builtins-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp; $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<)).builtins' >> $@.tmp;
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp @cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/builtins.json: $(bindir)/nix $(d)/src/language/builtin-constants.md: $(d)/language.json $(d)/generate-builtin-constants.nix $(d)/src/language/builtin-constants-prefix.md $(bindir)/nix
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-builtins > $@.tmp @cat doc/manual/src/language/builtin-constants-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtin-constants.nix (builtins.fromJSON (builtins.readFile $<)).constants' >> $@.tmp;
@cat doc/manual/src/language/builtin-constants-suffix.md >> $@.tmp
@mv $@.tmp $@
$(d)/language.json: $(bindir)/nix
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-language > $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
# Generate the HTML manual. # Generate the HTML manual.
@ -167,7 +173,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
done done
@touch $@ @touch $@
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md
$(trace-gen) \ $(trace-gen) \
tmp="$$(mktemp -d)"; \ tmp="$$(mktemp -d)"; \
cp -r doc/manual "$$tmp"; \ cp -r doc/manual "$$tmp"; \

View file

@ -330,17 +330,31 @@ const redirects = {
"ssec-relnotes-2.0": "release-notes/rl-2.0.html", "ssec-relnotes-2.0": "release-notes/rl-2.0.html",
"ssec-relnotes-2.1": "release-notes/rl-2.1.html", "ssec-relnotes-2.1": "release-notes/rl-2.1.html",
"ssec-relnotes-2.2": "release-notes/rl-2.2.html", "ssec-relnotes-2.2": "release-notes/rl-2.2.html",
"ssec-relnotes-2.3": "release-notes/rl-2.3.html" "ssec-relnotes-2.3": "release-notes/rl-2.3.html",
}, },
"language/values.html": { "language/values.html": {
"simple-values": "#primitives", "simple-values": "#primitives",
"lists": "#list", "lists": "#list",
"strings": "#string", "strings": "#string",
"lists": "#list", "lists": "#list",
"attribute-sets": "#attribute-set" "attribute-sets": "#attribute-set",
}, },
"installation/installing-binary.html": { "installation/installing-binary.html": {
"uninstalling": "uninstall.html" "linux": "uninstall.html#linux",
"macos": "uninstall.html#macos",
"uninstalling": "uninstall.html",
}
"contributing/hacking.html": {
"nix-with-flakes": "#building-nix-with-flakes",
"classic-nix": "#building-nix",
"running-tests": "testing.html#running-tests",
"unit-tests": "testing.html#unit-tests",
"functional-tests": "testing.html#functional-tests",
"debugging-failing-functional-tests": "testing.html#debugging-failing-functional-tests",
"integration-tests": "testing.html#integration-tests",
"installer-tests": "testing.html#installer-tests",
"one-time-setup": "testing.html#one-time-setup",
"using-the-ci-generated-installer-for-manual-testing": "testing.html#using-the-ci-generated-installer-for-manual-testing",
} }
}; };

View file

@ -97,14 +97,20 @@
- [manifest.json](command-ref/files/manifest.json.md) - [manifest.json](command-ref/files/manifest.json.md)
- [Channels](command-ref/files/channels.md) - [Channels](command-ref/files/channels.md)
- [Default Nix expression](command-ref/files/default-nix-expression.md) - [Default Nix expression](command-ref/files/default-nix-expression.md)
- [Architecture](architecture/architecture.md) - [Architecture and Design](architecture/architecture.md)
- [File System Object](architecture/file-system-object.md)
- [Protocols](protocols/protocols.md)
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
- [Glossary](glossary.md) - [Glossary](glossary.md)
- [Contributing](contributing/contributing.md) - [Contributing](contributing/contributing.md)
- [Hacking](contributing/hacking.md) - [Hacking](contributing/hacking.md)
- [Testing](contributing/testing.md)
- [Experimental Features](contributing/experimental-features.md) - [Experimental Features](contributing/experimental-features.md)
- [CLI guideline](contributing/cli-guideline.md) - [CLI guideline](contributing/cli-guideline.md)
- [C++ style guide](contributing/cxx.md)
- [Release Notes](release-notes/release-notes.md) - [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md) - [Release X.Y (202?-??-??)](release-notes/rl-next.md)
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)
- [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md) - [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md)
- [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md) - [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md)
- [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md) - [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md)

View file

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

View file

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

View file

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

View file

@ -7,11 +7,11 @@ It should help users understand why Nix behaves as it does, and it should help d
Nix consists of [hierarchical layers]. Nix consists of [hierarchical layers].
[hierarchical layers]: https://en.m.wikipedia.org/wiki/Multitier_architecture#Layers [hierarchical layers]: https://en.wikipedia.org/wiki/Multitier_architecture#Layers
The following [concept map] shows its main components (rectangles), the objects they operate on (rounded rectangles), and their interactions (connecting phrases): The following [concept map] shows its main components (rectangles), the objects they operate on (rounded rectangles), and their interactions (connecting phrases):
[concept map]: https://en.m.wikipedia.org/wiki/Concept_map [concept map]: https://en.wikipedia.org/wiki/Concept_map
``` ```
@ -76,7 +76,7 @@ The result of a build task can be input to another build task.
The following [data flow diagram] shows a build plan for illustration. The following [data flow diagram] shows a build plan for illustration.
Build inputs used as instructions to a build task are marked accordingly: Build inputs used as instructions to a build task are marked accordingly:
[data flow diagram]: https://en.m.wikipedia.org/wiki/Data-flow_diagram [data flow diagram]: https://en.wikipedia.org/wiki/Data-flow_diagram
``` ```
+--------------------------------------------------------------------+ +--------------------------------------------------------------------+

View file

@ -0,0 +1,64 @@
# File System Object
Nix uses a simplified model of the file system, which consists of file system objects.
Every file system object is one of the following:
- File
- A possibly empty sequence of bytes for contents
- A single boolean representing the [executable](https://en.m.wikipedia.org/wiki/File-system_permissions#Permissions) permission
- Directory
Mapping of names to child file system objects
- [Symbolic link](https://en.m.wikipedia.org/wiki/Symbolic_link)
An arbitrary string.
Nix does not assign any semantics to symbolic links.
File system objects and their children form a tree.
A bare file or symlink can be a root file system object.
Nix does not encode any other file system notions such as [hard links](https://en.m.wikipedia.org/wiki/Hard_link), [permissions](https://en.m.wikipedia.org/wiki/File-system_permissions), timestamps, or other metadata.
## Examples of file system objects
A plain file:
```
50 B, executable: false
```
An executable file:
```
122 KB, executable: true
```
A symlink:
```
-> /usr/bin/sh
```
A directory with contents:
```
├── bin
│   └── hello: 35 KB, executable: true
└── share
├── info
│   └── hello.info: 36 KB, executable: false
└── man
└── man1
└── hello.1.gz: 790 B, executable: false
```
A directory that contains a symlink and other directories:
```
├── bin -> share/go/bin
├── nix-support/
└── share/
```

View file

@ -4,49 +4,67 @@
# Description # Description
By default Nix reads settings from the following places: Nix supports a variety of configuration settings, which are read from configuration files or taken as command line flags.
- The system-wide configuration file `sysconfdir/nix/nix.conf` (i.e. ## Configuration file
`/etc/nix/nix.conf` on most systems), or `$NIX_CONF_DIR/nix.conf` if
`NIX_CONF_DIR` is set. Values loaded in this file are not forwarded
to the Nix daemon. The client assumes that the daemon has already
loaded them.
- If `NIX_USER_CONF_FILES` is set, then each path separated by `:` By default Nix reads settings from the following places, in that order:
will be loaded in reverse order.
Otherwise it will look for `nix/nix.conf` files in `XDG_CONFIG_DIRS` 1. The system-wide configuration file `sysconfdir/nix/nix.conf` (i.e. `/etc/nix/nix.conf` on most systems), or `$NIX_CONF_DIR/nix.conf` if [`NIX_CONF_DIR`](./env-common.md#env-NIX_CONF_DIR) is set.
and `XDG_CONFIG_HOME`. If unset, `XDG_CONFIG_DIRS` defaults to
`/etc/xdg`, and `XDG_CONFIG_HOME` defaults to `$HOME/.config`
as per [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
- If `NIX_CONFIG` is set, its contents is treated as the contents of Values loaded in this file are not forwarded to the Nix daemon.
a configuration file. The client assumes that the daemon has already loaded them.
The configuration files consist of `name = value` pairs, one per 1. If [`NIX_USER_CONF_FILES`](./env-common.md#env-NIX_USER_CONF_FILES) is set, then each path separated by `:` will be loaded in reverse order.
line. Other files can be included with a line like `include path`,
where *path* is interpreted relative to the current conf file and a
missing file is an error unless `!include` is used instead. Comments
start with a `#` character. Here is an example configuration file:
keep-outputs = true # Nice for developers Otherwise it will look for `nix/nix.conf` files in `XDG_CONFIG_DIRS` and [`XDG_CONFIG_HOME`](./env-common.md#env-XDG_CONFIG_HOME).
keep-derivations = true # Idem If unset, `XDG_CONFIG_DIRS` defaults to `/etc/xdg`, and `XDG_CONFIG_HOME` defaults to `$HOME/.config` as per [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
You can override settings on the command line using the `--option` 1. If [`NIX_CONFIG`](./env-common.md#env-NIX_CONFIG) is set, its contents are treated as the contents of a configuration file.
flag, e.g. `--option keep-outputs false`. Every configuration setting
also has a corresponding command line flag, e.g. `--max-jobs 16`; for
Boolean settings, there are two flags to enable or disable the setting
(e.g. `--keep-failed` and `--no-keep-failed`).
A configuration setting usually overrides any previous value. However, ### File format
you can prefix the name of the setting by `extra-` to *append* to the
previous value. For instance,
substituters = a b Configuration files consist of `name = value` pairs, one per line.
extra-substituters = c d Comments start with a `#` character.
defines the `substituters` setting to be `a b c d`. This is also Example:
available as a command line flag (e.g. `--extra-substituters`).
The following settings are currently available: ```
keep-outputs = true # Nice for developers
keep-derivations = true # Idem
```
Other files can be included with a line like `include <path>`, where `<path>` is interpreted relative to the current configuration file.
A missing file is an error unless `!include` is used instead.
A configuration setting usually overrides any previous value.
However, for settings that take a list of items, you can prefix the name of the setting by `extra-` to *append* to the previous value.
For instance,
```
substituters = a b
extra-substituters = c d
```
defines the `substituters` setting to be `a b c d`.
Unknown option names are not an error, and are simply ignored with a warning.
## Command line flags
Configuration options can be set on the command line, overriding the values set in the [configuration file](#configuration-file):
- Every configuration setting has corresponding command line flag (e.g. `--max-jobs 16`).
Boolean settings do not need an argument, and can be explicitly disabled with the `no-` prefix (e.g. `--keep-failed` and `--no-keep-failed`).
Unknown option names are invalid flags (unless there is already a flag with that name), and are rejected with an error.
- The flag `--option <name> <value>` is interpreted exactly like a `<name> = <value>` in a setting file.
Unknown option names are ignored with a warning.
The `extra-` prefix is supported for settings that take a list of items (e.g. `--extra-trusted users alice` or `--option extra-trusted-users alice`).
# Available settings

View file

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

View file

@ -51,8 +51,9 @@ derivation).
# Options # Options
All options not listed here are passed to `nix-store --realise`, All options not listed here are passed to
except for `--arg` and `--attr` / `-A` which are passed to `nix-instantiate`. [`nix-store --realise`](nix-store/realise.md),
except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](nix-instantiate.md).
- <span id="opt-no-out-link">[`--no-out-link`](#opt-no-out-link)<span> - <span id="opt-no-out-link">[`--no-out-link`](#opt-no-out-link)<span>
@ -69,6 +70,8 @@ except for `--arg` and `--attr` / `-A` which are passed to `nix-instantiate`.
Change the name of the symlink to the output path created from Change the name of the symlink to the output path created from
`result` to *outlink*. `result` to *outlink*.
{{#include ./status-build-failure.md}}
{{#include ./opt-common.md}} {{#include ./opt-common.md}}
{{#include ./env-common.md}} {{#include ./env-common.md}}
@ -76,7 +79,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 +94,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 +104,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 +112,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 +121,5 @@ Build the GNU Hello package from the latest revision of the master
branch of Nixpkgs: branch of Nixpkgs:
```console ```console
$ nix-build https://github.com/NixOS/nixpkgs/archive/master.tar.gz -A hello $ nix-build https://github.com/NixOS/nixpkgs/archive/master.tar.gz --attr hello
``` ```

View file

@ -4,7 +4,7 @@
# Synopsis # Synopsis
`nix-channel` {`--add` url [*name*] | `--remove` *name* | `--list` | `--update` [*names…*] | `--rollback` [*generation*] } `nix-channel` {`--add` url [*name*] | `--remove` *name* | `--list` | `--update` [*names…*] | `--list-generations` | `--rollback` [*generation*] }
# Description # Description
@ -39,6 +39,15 @@ This command has the following operations:
for `nix-env` operations (by symlinking them from the directory for `nix-env` operations (by symlinking them from the directory
`~/.nix-defexpr`). `~/.nix-defexpr`).
- `--list-generations`\
Prints a list of all the current existing generations for the
channel profile.
Works the same way as
```
nix-env --profile /nix/var/nix/profiles/per-user/$USER/channels --list-generations
```
- `--rollback` \[*generation*\]\ - `--rollback` \[*generation*\]\
Reverts the previous call to `nix-channel Reverts the previous call to `nix-channel
--update`. Optionally, you can specify a specific channel generation --update`. Optionally, you can specify a specific channel generation
@ -52,6 +61,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:
@ -59,18 +74,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"
``` ```

View file

@ -1,6 +1,6 @@
# Name # Name
`nix-collect-garbage` - delete unreachable store paths `nix-collect-garbage` - delete unreachable [store objects]
# Synopsis # Synopsis
@ -8,17 +8,57 @@
# Description # Description
The command `nix-collect-garbage` is mostly an alias of [`nix-store The command `nix-collect-garbage` is mostly an alias of [`nix-store --gc`](@docroot@/command-ref/nix-store/gc.md).
--gc`](@docroot@/command-ref/nix-store/gc.md), that is, it deletes all That is, it deletes all unreachable [store objects] in the Nix store to clean up your system.
unreachable paths in the Nix store to clean up your system. However,
it provides two additional options: `-d` (`--delete-old`), which However, it provides two additional options,
deletes all old generations of all profiles in `/nix/var/nix/profiles` [`--delete-old`](#opt-delete-old) and [`--delete-older-than`](#opt-delete-older-than),
by invoking `nix-env --delete-generations old` on all profiles (of which also delete old [profiles], allowing potentially more [store objects] to be deleted because profiles are also garbage collection roots.
course, this makes rollbacks to previous configurations impossible); These options are the equivalent of running
and `--delete-older-than` *period*, where period is a value such as [`nix-env --delete-generations`](@docroot@/command-ref/nix-env/delete-generations.md)
`30d`, which deletes all generations older than the specified number with various augments on multiple profiles,
of days in all profiles in `/nix/var/nix/profiles` (except for the prior to running `nix-collect-garbage` (or just `nix-store --gc`) without any flags.
generations that were active at that point in time).
> **Note**
>
> Deleting previous configurations makes rollbacks to them impossible.
These flags should be used with care, because they potentially delete generations of profiles used by other users on the system.
## Locations searched for profiles
`nix-collect-garbage` cannot know about all profiles; that information doesn't exist.
Instead, it looks in a few locations, and acts on all profiles it finds there:
1. The default profile locations as specified in the [profiles] section of the manual.
2. > **NOTE**
>
> Not stable; subject to change
>
> Do not rely on this functionality; it just exists for migration purposes and is may change in the future.
> These deprecated paths remain a private implementation detail of Nix.
`$NIX_STATE_DIR/profiles` and `$NIX_STATE_DIR/profiles/per-user`.
With the exception of `$NIX_STATE_DIR/profiles/per-user/root` and `$NIX_STATE_DIR/profiles/default`, these directories are no longer used by other commands.
`nix-collect-garbage` looks there anyways in order to clean up profiles from older versions of Nix.
# Options
These options are for deleting old [profiles] prior to deleting unreachable [store objects].
- <span id="opt-delete-old">[`--delete-old`](#opt-delete-old)</span> / `-d`\
Delete all old generations of profiles.
This is the equivalent of invoking `nix-env --delete-generations old` on each found profile.
- <span id="opt-delete-older-than">[`--delete-older-than`](#opt-delete-older-than)</span> *period*\
Delete all generations of profiles older than the specified amount (except for the generations that were active at that point in time).
*period* is a value such as `30d`, which would mean 30 days.
This is the equivalent of invoking [`nix-env --delete-generations <period>`](@docroot@/command-ref/nix-env/delete-generations.md#generations-time) on each found profile.
See the documentation of that command for additional information about the *period* argument.
{{#include ./opt-common.md}} {{#include ./opt-common.md}}
@ -32,3 +72,6 @@ generations of each profile, do
```console ```console
$ nix-collect-garbage -d $ nix-collect-garbage -d
``` ```
[profiles]: @docroot@/command-ref/files/profiles.md
[store objects]: @docroot@/glossary.md#gloss-store-object

View file

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

View file

@ -49,7 +49,7 @@ These pages can be viewed offline:
# Selectors # Selectors
Several commands, such as `nix-env -q` and `nix-env -i`, take a list of Several commands, such as `nix-env --query ` and `nix-env --install `, take a list of
arguments that specify the packages on which to operate. These are arguments that specify the packages on which to operate. These are
extended regular expressions that must match the entire name of the extended regular expressions that must match the entire name of the
package. (For details on regular expressions, see **regex**(7).) The match is package. (For details on regular expressions, see **regex**(7).) The match is
@ -83,6 +83,8 @@ match. Here are some examples:
# Files # Files
`nix-env` operates on the following files.
{{#include ./files/default-nix-expression.md}} {{#include ./files/default-nix-expression.md}}
{{#include ./files/profiles.md}} {{#include ./files/profiles.md}}

View file

@ -9,14 +9,47 @@
# Description # Description
This operation deletes the specified generations of the current profile. This operation deletes the specified generations of the current profile.
The generations can be a list of generation numbers, the special value
`old` to delete all non-current generations, a value such as `30d` to *generations* can be a one of the following:
delete all generations older than the specified number of days (except
for the generation that was active at that point in time), or a value - <span id="generations-list">`<number>...`</span>:\
such as `+5` to keep the last `5` generations ignoring any newer than A list of generation numbers, each one a separate command-line argument.
current, e.g., if `30` is the current generation `+5` will delete
generation `25` and all older generations. Periodically deleting old Delete exactly the profile generations given by their generation number.
generations is important to make garbage collection effective. Deleting the current generation is not allowed.
- The special value <span id="generations-old">`old`</span>
Delete all generations except the current one.
> **WARNING**
>
> Older *and newer* generations will be deleted by this operation.
>
> One might expect this to just delete older generations than the curent one, but that is only true if the current generation is also the latest.
> Because one can roll back to a previous generation, it is possible to have generations newer than the current one.
> They will also be deleted.
- <span id="generations-time">`<number>d`</span>:\
The last *number* days
*Example*: `30d`
Delete all generations created more than *number* days ago, except the most recent one of them.
This allows rolling back to generations that were available within the specified period.
- <span id="generations-count">`+<number>`</span>:\
The last *number* generations up to the present
*Example*: `+5`
Keep the last *number* generations, along with any newer than current.
Periodically deleting old generations is important to make garbage collection
effective.
The is because profiles are also garbage collection roots — any [store object] reachable from a profile is "alive" and ineligible for deletion.
[store object]: @docroot@/glossary.md#gloss-store-object
{{#include ./opt-common.md}} {{#include ./opt-common.md}}
@ -28,19 +61,35 @@ generations is important to make garbage collection effective.
# Examples # Examples
## Delete explicit generation numbers
```console ```console
$ nix-env --delete-generations 3 4 8 $ nix-env --delete-generations 3 4 8
``` ```
Delete the generations numbered 3, 4, and 8, so long as the current active generation is not any of those.
## Keep most-recent by count (number of generations)
```console ```console
$ nix-env --delete-generations +5 $ nix-env --delete-generations +5
``` ```
Suppose `30` is the current generation, and we currently have generations numbered `20` through `32`.
Then this command will delete generations `20` through `25` (`<= 30 - 5`),
and keep generations `26` through `31` (`> 30 - 5`).
## Keep most-recent by time (number of days)
```console ```console
$ nix-env --delete-generations 30d $ nix-env --delete-generations 30d
``` ```
```console This command will delete all generations older than 30 days, except for the generation that was active 30 days ago (if it currently exists).
$ nix-env -p other_profile --delete-generations old
```
## Delete all older
```console
$ nix-env --profile other_profile --delete-generations old
```

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -54,36 +54,7 @@ The following flags are available:
previous build, the new output path is left in previous build, the new output path is left in
`/nix/store/name.check.` `/nix/store/name.check.`
Special exit codes: {{#include ../status-build-failure.md}}
- `100`\
Generic build failure, the builder process returned with a non-zero
exit code.
- `101`\
Build timeout, the build was aborted because it did not complete
within the specified `timeout`.
- `102`\
Hash mismatch, the build output was rejected because it does not
match the [`outputHash` attribute of the
derivation](@docroot@/language/advanced-attributes.md).
- `104`\
Not deterministic, the build succeeded in check mode but the
resulting output is not binary reproducible.
With the `--keep-going` flag it's possible for multiple failures to
occur, in this case the 1xx status codes are or combined using binary
or.
1100100
^^^^
|||`- timeout
||`-- output hash mismatch
|`--- build failure
`---- not deterministic
{{#include ./opt-common.md}} {{#include ./opt-common.md}}
@ -99,7 +70,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 +79,7 @@ This is essentially what [`nix-build`](@docroot@/command-ref/nix-build.md) does.
To test whether a previously-built derivation is deterministic: To test whether a previously-built derivation is deterministic:
```console ```console
$ nix-build '<nixpkgs>' -A hello --check -K $ nix-build '<nixpkgs>' --attr hello --check -K
``` ```
Use [`nix-store --read-log`](./read-log.md) to show the stderr and stdout of a build: Use [`nix-store --read-log`](./read-log.md) to show the stderr and stdout of a build:

View file

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

View file

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

View file

@ -0,0 +1,34 @@
# Special exit codes for build failure
1xx status codes are used when requested builds failed.
The following codes are in use:
- `100` Generic build failure
The builder process returned with a non-zero exit code.
- `101` Build timeout
The build was aborted because it did not complete within the specified `timeout`.
- `102` Hash mismatch
The build output was rejected because it does not match the
[`outputHash` attribute of the derivation](@docroot@/language/advanced-attributes.md).
- `104` Not deterministic
The build succeeded in check mode but the resulting output is not binary reproducible.
With the `--keep-going` flag it's possible for multiple failures to occur.
In this case the 1xx status codes are or combined using
[bitwise OR](https://en.wikipedia.org/wiki/Bitwise_operation#OR).
```
0b1100100
^^^^
|||`- timeout
||`-- output hash mismatch
|`--- build failure
`---- not deterministic
```

View file

@ -0,0 +1,28 @@
# C++ style guide
Some miscellaneous notes on how we write C++.
Formatting we hope to eventually normalize automatically, so this section is free to just discuss higher-level concerns.
## The `*-impl.hh` pattern
Let's start with some background info first.
Headers, are supposed to contain declarations, not definitions.
This allows us to change a definition without changing the declaration, and have a very small rebuild during development.
Templates, however, need to be specialized to use-sites.
Absent fancier techniques, templates require that the definition, not just mere declaration, must be available at use-sites in order to make that specialization on the fly as part of compiling those use-sites.
Making definitions available like that means putting them in headers, but that is unfortunately means we get all the extra rebuilds we want to avoid by just putting declarations there as described above.
The `*-impl.hh` pattern is a ham-fisted partial solution to this problem.
It constitutes:
- Declaring items only in the main `foo.hh`, including templates.
- Putting template definitions in a companion `foo-impl.hh` header.
Most C++ developers would accompany this by having `foo.hh` include `foo-impl.hh`, to ensure any file getting the template declarations also got the template definitions.
But we've found not doing this has some benefits and fewer than imagined downsides.
The fact remains that headers are rarely as minimal as they could be;
there is often code that needs declarations from the headers but not the templates within them.
With our pattern where `foo.hh` doesn't include `foo-impl.hh`, that means they can just include `foo.hh`
Code that needs both just includes `foo.hh` and `foo-impl.hh`.
This does make linking error possible where something forgets to include `foo-impl.hh` that needs it, but those are build-time only as easy to fix.

View file

@ -12,14 +12,15 @@ The following instructions assume you already have some version of Nix installed
[installation instructions]: ../installation/installation.md [installation instructions]: ../installation/installation.md
## Nix with flakes ## Building Nix with flakes
This section assumes you are using Nix with [flakes] enabled. See the [next section](#classic-nix) for equivalent instructions which don't require flakes. This section assumes you are using Nix with the [`flakes`] and [`nix-command`] experimental features enabled.
See the [Building Nix](#building-nix) section for equivalent instructions using stable Nix interfaces.
[flakes]: ../command-ref/new-cli/nix3-flake.md#description [`flakes`]: @docroot@/contributing/experimental-features.md#xp-feature-flakes
[`nix-command`]: @docroot@/contributing/experimental-features.md#xp-nix-command
To build all dependencies and start a shell in which all environment To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found:
variables are set up so that those dependencies can be found:
```console ```console
$ nix develop $ nix develop
@ -55,20 +56,17 @@ To install it in `$(pwd)/outputs` and test it:
nix (Nix) 2.12 nix (Nix) 2.12
``` ```
To build a release version of Nix: To build a release version of Nix for the current operating system and CPU architecture:
```console ```console
$ nix build $ nix build
``` ```
You can also build Nix for one of the [supported target platforms](#target-platforms). You can also build Nix for one of the [supported platforms](#platforms).
## Classic Nix ## Building Nix
This section is for Nix without [flakes]. To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found:
To build all dependencies and start a shell in which all environment
variables are set up so that those dependencies can be found:
```console ```console
$ nix-shell $ nix-shell
@ -77,7 +75,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**
@ -102,13 +100,13 @@ To install it in `$(pwd)/outputs` and test it:
nix (Nix) 2.12 nix (Nix) 2.12
``` ```
To build Nix for the current operating system and CPU architecture use To build a release version of Nix for the current operating system and CPU architecture:
```console ```console
$ nix-build $ nix-build
``` ```
You can also build Nix for one of the [supported target platforms](#target-platforms). You can also build Nix for one of the [supported platforms](#platforms).
## Platforms ## Platforms
@ -139,7 +137,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 +164,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.
@ -192,171 +190,6 @@ Configure your editor to use the `clangd` from the shell, either by running it i
> Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim). > Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim).
> Editor-specific setup is typically opinionated, so we will not cover it here in more detail. > Editor-specific setup is typically opinionated, so we will not cover it here in more detail.
## Running tests
### Unit-tests
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
under `src/{library_name}/tests` using the
[googletest](https://google.github.io/googletest/) and
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option.
### Functional tests
The functional tests reside under the `tests` directory and are listed in `tests/local.mk`.
Each test is a bash script.
The whole test suite can be run with:
```shell-session
$ make install && make installcheck
ran test tests/foo.sh... [PASS]
ran test tests/bar.sh... [PASS]
...
```
Individual tests can be run with `make`:
```shell-session
$ make tests/${testName}.sh.test
ran test tests/${testName}.sh... [PASS]
```
or without `make`:
```shell-session
$ ./mk/run-test.sh tests/${testName}.sh
ran test tests/${testName}.sh... [PASS]
```
To see the complete output, one can also run:
```shell-session
$ ./mk/debug-test.sh tests/${testName}.sh
+ foo
output from foo
+ bar
output from bar
...
```
The test script will then be traced with `set -x` and the output displayed as it happens, regardless of whether the test succeeds or fails.
#### Debugging failing functional tests
When a functional test fails, it usually does so somewhere in the middle of the script.
To figure out what's wrong, it is convenient to run the test regularly up to the failing `nix` command, and then run that command with a debugger like GDB.
For example, if the script looks like:
```bash
foo
nix blah blub
bar
```
edit it like so:
```diff
foo
-nix blah blub
+gdb --args nix blah blub
bar
```
Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point:
```shell-session
$ ./mk/debug-test.sh tests/${testName}.sh
...
+ gdb blash blub
GNU gdb (GDB) 12.1
...
(gdb)
```
One can debug the Nix invocation in all the usual ways.
For example, enter `run` to start the Nix invocation.
### Integration tests
The integration tests are defined in the Nix flake under the `hydraJobs.tests` attribute.
These tests include everything that needs to interact with external services or run Nix in a non-trivial distributed setup.
Because these tests are expensive and require more than what the standard github-actions setup provides, they only run on the master branch (on <https://hydra.nixos.org/jobset/nix/master>).
You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-build -A hydraJobs.tests.{testName}`
### Installer tests
After a one-time setup, the Nix repository's GitHub Actions continuous integration (CI) workflow can test the installer each time you push to a branch.
Creating a Cachix cache for your installer tests and adding its authorization token to GitHub enables [two installer-specific jobs in the CI workflow](https://github.com/NixOS/nix/blob/88a45d6149c0e304f6eb2efcc2d7a4d0d569f8af/.github/workflows/ci.yml#L50-L91):
- The `installer` job generates installers for the platforms below and uploads them to your Cachix cache:
- `x86_64-linux`
- `armv6l-linux`
- `armv7l-linux`
- `x86_64-darwin`
- The `installer_test` job (which runs on `ubuntu-latest` and `macos-latest`) will try to install Nix with the cached installer and run a trivial Nix command.
#### One-time setup
1. Have a GitHub account with a fork of the [Nix repository](https://github.com/NixOS/nix).
2. At cachix.org:
- Create or log in to an account.
- Create a Cachix cache using the format `<github-username>-nix-install-tests`.
- Navigate to the new cache > Settings > Auth Tokens.
- Generate a new Cachix auth token and copy the generated value.
3. At github.com:
- Navigate to your Nix fork > Settings > Secrets > Actions > New repository secret.
- Name the secret `CACHIX_AUTH_TOKEN`.
- Paste the copied value of the Cachix cache auth token.
#### Using the CI-generated installer for manual testing
After the CI run completes, you can check the output to extract the installer URL:
1. Click into the detailed view of the CI run.
2. Click into any `installer_test` run (the URL you're here to extract will be the same in all of them).
3. Click into the `Run cachix/install-nix-action@v...` step and click the detail triangle next to the first log line (it will also be `Run cachix/install-nix-action@v...`)
4. Copy the value of `install_url`
5. To generate an install command, plug this `install_url` and your GitHub username into this template:
```console
curl -L <install_url> | sh -s -- --tarball-url-prefix https://<github-username>-nix-install-tests.cachix.org/serve
```
<!-- #### Manually generating test installers
There's obviously a manual way to do this, and it's still the only way for
platforms that lack GA runners.
I did do this back in Fall 2020 (before the GA approach encouraged here). I'll
sketch what I recall in case it encourages someone to fill in detail, but: I
didn't know what I was doing at the time and had to fumble/ask around a lot--
so I don't want to uphold any of it as "right". It may have been dumb or
the _hard_ way from the getgo. Fundamentals may have changed since.
Here's the build command I used to do this on and for x86_64-darwin:
nix build --out-link /tmp/foo ".#checks.x86_64-darwin.binaryTarball"
I used the stable out-link to make it easier to script the next steps:
link=$(readlink /tmp/foo)
cp $link/*-darwin.tar.xz ~/somewheres
I've lost the last steps and am just going from memory:
From here, I think I had to extract and modify the `install` script to point
it at this tarball (which I scped to my own site, but it might make more sense
to just share them locally). I extracted this script once and then just
search/replaced in it for each new build.
The installer now supports a `--tarball-url-prefix` flag which _may_ have
solved this need?
-->
### Checking links in the manual ### Checking links in the manual
The build checks for broken internal links. The build checks for broken internal links.
@ -378,7 +211,7 @@ rm $(git ls-files doc/manual/ -o | grep -F '.md') && rmdir doc/manual/src/comman
[`mdbook-linkcheck`] does not implement checking [URI fragments] yet. [`mdbook-linkcheck`] does not implement checking [URI fragments] yet.
[`mdbook-linkcheck`]: https://github.com/Michael-F-Bryan/mdbook-linkcheck [`mdbook-linkcheck`]: https://github.com/Michael-F-Bryan/mdbook-linkcheck
[URI fragments]: https://en.m.wikipedia.org/wiki/URI_fragment [URI fragments]: https://en.wikipedia.org/wiki/URI_fragment
#### `@docroot@` variable #### `@docroot@` variable

View file

@ -0,0 +1,167 @@
# Running tests
## Unit-tests
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
under `src/{library_name}/tests` using the
[googletest](https://google.github.io/googletest/) and
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option.
## Functional tests
The functional tests reside under the `tests` directory and are listed in `tests/local.mk`.
Each test is a bash script.
The whole test suite can be run with:
```shell-session
$ make install && make installcheck
ran test tests/foo.sh... [PASS]
ran test tests/bar.sh... [PASS]
...
```
Individual tests can be run with `make`:
```shell-session
$ make tests/${testName}.sh.test
ran test tests/${testName}.sh... [PASS]
```
or without `make`:
```shell-session
$ ./mk/run-test.sh tests/${testName}.sh
ran test tests/${testName}.sh... [PASS]
```
To see the complete output, one can also run:
```shell-session
$ ./mk/debug-test.sh tests/${testName}.sh
+ foo
output from foo
+ bar
output from bar
...
```
The test script will then be traced with `set -x` and the output displayed as it happens, regardless of whether the test succeeds or fails.
### Debugging failing functional tests
When a functional test fails, it usually does so somewhere in the middle of the script.
To figure out what's wrong, it is convenient to run the test regularly up to the failing `nix` command, and then run that command with a debugger like GDB.
For example, if the script looks like:
```bash
foo
nix blah blub
bar
```
edit it like so:
```diff
foo
-nix blah blub
+gdb --args nix blah blub
bar
```
Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point:
```shell-session
$ ./mk/debug-test.sh tests/${testName}.sh
...
+ gdb blash blub
GNU gdb (GDB) 12.1
...
(gdb)
```
One can debug the Nix invocation in all the usual ways.
For example, enter `run` to start the Nix invocation.
## Integration tests
The integration tests are defined in the Nix flake under the `hydraJobs.tests` attribute.
These tests include everything that needs to interact with external services or run Nix in a non-trivial distributed setup.
Because these tests are expensive and require more than what the standard github-actions setup provides, they only run on the master branch (on <https://hydra.nixos.org/jobset/nix/master>).
You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-build -A hydraJobs.tests.{testName}`
## Installer tests
After a one-time setup, the Nix repository's GitHub Actions continuous integration (CI) workflow can test the installer each time you push to a branch.
Creating a Cachix cache for your installer tests and adding its authorization token to GitHub enables [two installer-specific jobs in the CI workflow](https://github.com/NixOS/nix/blob/88a45d6149c0e304f6eb2efcc2d7a4d0d569f8af/.github/workflows/ci.yml#L50-L91):
- The `installer` job generates installers for the platforms below and uploads them to your Cachix cache:
- `x86_64-linux`
- `armv6l-linux`
- `armv7l-linux`
- `x86_64-darwin`
- The `installer_test` job (which runs on `ubuntu-latest` and `macos-latest`) will try to install Nix with the cached installer and run a trivial Nix command.
### One-time setup
1. Have a GitHub account with a fork of the [Nix repository](https://github.com/NixOS/nix).
2. At cachix.org:
- Create or log in to an account.
- Create a Cachix cache using the format `<github-username>-nix-install-tests`.
- Navigate to the new cache > Settings > Auth Tokens.
- Generate a new Cachix auth token and copy the generated value.
3. At github.com:
- Navigate to your Nix fork > Settings > Secrets > Actions > New repository secret.
- Name the secret `CACHIX_AUTH_TOKEN`.
- Paste the copied value of the Cachix cache auth token.
## Working on documentation
### Using the CI-generated installer for manual testing
After the CI run completes, you can check the output to extract the installer URL:
1. Click into the detailed view of the CI run.
2. Click into any `installer_test` run (the URL you're here to extract will be the same in all of them).
3. Click into the `Run cachix/install-nix-action@v...` step and click the detail triangle next to the first log line (it will also be `Run cachix/install-nix-action@v...`)
4. Copy the value of `install_url`
5. To generate an install command, plug this `install_url` and your GitHub username into this template:
```console
curl -L <install_url> | sh -s -- --tarball-url-prefix https://<github-username>-nix-install-tests.cachix.org/serve
```
<!-- #### Manually generating test installers
There's obviously a manual way to do this, and it's still the only way for
platforms that lack GA runners.
I did do this back in Fall 2020 (before the GA approach encouraged here). I'll
sketch what I recall in case it encourages someone to fill in detail, but: I
didn't know what I was doing at the time and had to fumble/ask around a lot--
so I don't want to uphold any of it as "right". It may have been dumb or
the _hard_ way from the getgo. Fundamentals may have changed since.
Here's the build command I used to do this on and for x86_64-darwin:
nix build --out-link /tmp/foo ".#checks.x86_64-darwin.binaryTarball"
I used the stable out-link to make it easier to script the next steps:
link=$(readlink /tmp/foo)
cp $link/*-darwin.tar.xz ~/somewheres
I've lost the last steps and am just going from memory:
From here, I think I had to extract and modify the `install` script to point
it at this tarball (which I scped to my own site, but it might make more sense
to just share them locally). I extracted this script once and then just
search/replaced in it for each new build.
The installer now supports a `--tarball-url-prefix` flag which _may_ have
solved this need?
-->

View file

@ -85,12 +85,17 @@
[store path]: #gloss-store-path [store path]: #gloss-store-path
- [file system object]{#gloss-store-object}\
The Nix data model for representing simplified file system data.
See [File System Object](@docroot@/architecture/file-system-object.md) for details.
[file system object]: #gloss-file-system-object
- [store object]{#gloss-store-object}\ - [store object]{#gloss-store-object}\
A file that is an immediate child of the Nix store directory. These
can be regular files, but also entire directory trees. Store objects A store object consists of a [file system object], [reference]s to other store objects, and other metadata.
can be sources (objects copied from outside of the store), It can be referred to by a [store path].
derivation outputs (objects produced by running a build task), or
derivations (files describing a build task).
[store object]: #gloss-store-object [store object]: #gloss-store-object
@ -101,11 +106,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
@ -115,9 +117,10 @@
from some server. from some server.
- [substituter]{#gloss-substituter}\ - [substituter]{#gloss-substituter}\
A *substituter* is an additional store from which Nix will An additional [store]{#gloss-store} from which Nix can obtain store objects instead of building them.
copy store objects it doesn't have. For details, see the Often the substituter is a [binary cache](#gloss-binary-cache), but any store can serve as substituter.
[`substituters` option](./command-ref/conf-file.md#conf-substituters).
See the [`substituters` configuration option](./command-ref/conf-file.md#conf-substituters) for details.
[substituter]: #gloss-substituter [substituter]: #gloss-substituter
@ -163,7 +166,7 @@
build-time dependencies, while the closure of its output path is build-time dependencies, while the closure of its output path is
equivalent to its runtime dependencies. For correct deployment it equivalent to its runtime dependencies. For correct deployment it
is necessary to deploy whole closures, since otherwise at runtime is necessary to deploy whole closures, since otherwise at runtime
files could be missing. The command `nix-store -qR` prints out files could be missing. The command `nix-store --query --requisites ` prints out
closures of store paths. closures of store paths.
As an example, if the [store object] at path `P` contains a [reference] As an example, if the [store object] at path `P` contains a [reference]

View file

@ -10,7 +10,7 @@
- Bash Shell. The `./configure` script relies on bashisms, so Bash is - Bash Shell. The `./configure` script relies on bashisms, so Bash is
required. required.
- A version of GCC or Clang that supports C++17. - A version of GCC or Clang that supports C++20.
- `pkg-config` to locate dependencies. If your distribution does not - `pkg-config` to locate dependencies. If your distribution does not
provide it, you can get it from provide it, you can get it from

View file

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

View file

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

View file

@ -0,0 +1,5 @@
# Built-in Constants
These constants are built into the Nix language evaluator:
<dl>

View file

@ -0,0 +1 @@
</dl>

View file

@ -1,19 +0,0 @@
# Built-in Constants
These constants are built into the Nix language evaluator:
- [`builtins`]{#builtins-builtins} (attribute set)
Contains all the [built-in functions](./builtins.md) and values, in order to avoid polluting the global scope.
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:
```nix
if builtins ? getEnv then builtins.getEnv "PATH" else ""
```
- [`builtins.currentSystem`]{#builtins-currentSystem} (string)
The built-in value `currentSystem` evaluates to the Nix platform
identifier for the Nix installation on which the expression is being
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.

View file

@ -3,7 +3,7 @@
This section lists the functions built into the Nix language evaluator. This section lists the functions built into the Nix language evaluator.
All built-in functions are available through the global [`builtins`](./builtin-constants.md#builtins-builtins) constant. All built-in functions are available through the global [`builtins`](./builtin-constants.md#builtins-builtins) constant.
For convenience, some built-ins are can be accessed directly: For convenience, some built-ins can be accessed directly:
- [`derivation`](#builtins-derivation) - [`derivation`](#builtins-derivation)
- [`import`](#builtins-import) - [`import`](#builtins-import)

View file

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

View file

@ -1,12 +1,11 @@
# Nix Language # Nix Language
The Nix language is The Nix language is designed for conveniently creating and composing *derivations* precise descriptions of how contents of existing files are used to derive new files.
It is:
- *domain-specific* - *domain-specific*
It only exists for the Nix package manager: It comes with [built-in functions](@docroot@/language/builtins.md) to integrate with the Nix store, which manages files and performs the derivations declared in the Nix language.
to describe packages and configurations as well as their variants and compositions.
It is not intended for general purpose use.
- *declarative* - *declarative*
@ -25,7 +24,7 @@ The Nix language is
- *lazy* - *lazy*
Expressions are only evaluated when their value is needed. Values are only computed when they are needed.
- *dynamically typed* - *dynamically typed*

View file

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

View file

@ -164,9 +164,17 @@ Note that lists are only lazy in values, and they are strict in length.
An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`).
An attribute name can be an identifier or a [string](#string).
An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`).
> *name* = *identifier* | *string* \
> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*`
Names and values are separated by an equal sign (`=`). Names and values are separated by an equal sign (`=`).
Each value is an arbitrary expression terminated by a semicolon (`;`). Each value is an arbitrary expression terminated by a semicolon (`;`).
> *attrset* = `{` [ *name* `=` *expr* `;` `]`... `}`
Attributes can appear in any order. Attributes can appear in any order.
An attribute name may only occur once. An attribute name may only occur once.
@ -182,15 +190,19 @@ 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:
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"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,4 @@
# Protocols
This chapter documents various developer-facing interfaces provided by
Nix.

View file

@ -0,0 +1,42 @@
# Lockable HTTP Tarball Protocol
Tarball flakes can be served as regular tarballs via HTTP or the file
system (for `file://` URLs). Unless the server implements the Lockable
HTTP Tarball protocol, it is the responsibility of the user to make sure that
the URL always produces the same tarball contents.
An HTTP server can return an "immutable" HTTP URL appropriate for lock
files. This allows users to specify a tarball flake input in
`flake.nix` that requests the latest version of a flake
(e.g. `https://example.org/hello/latest.tar.gz`), while `flake.lock`
will record a URL whose contents will not change
(e.g. `https://example.org/hello/<revision>.tar.gz`). To do so, the
server must return an [HTTP `Link` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link) with the `rel` attribute set to
`immutable`, as follows:
```
Link: <flakeref>; rel="immutable"
```
(Note the required `<` and `>` characters around *flakeref*.)
*flakeref* must be a tarball flakeref. It can contain flake attributes
such as `narHash`, `rev` and `revCount`. If `narHash` is included, its
value must be the NAR hash of the unpacked tarball (as computed via
`nix hash path`). Nix checks the contents of the returned tarball
against the `narHash` attribute. The `rev` and `revCount` attributes
are useful when the tarball flake is a mirror of a fetcher type that
has those attributes, such as Git or GitHub. They are not checked by
Nix.
```
Link: <https://example.org/hello/442793d9ec0584f6a6e82fa253850c8085bb150a.tar.gz
?rev=442793d9ec0584f6a6e82fa253850c8085bb150a
&revCount=835
&narHash=sha256-GUm8Uh/U74zFCwkvt9Mri4DSM%2BmHj3tYhXUkYpiv31M%3D>; rel="immutable"
```
(The linebreaks in this example are for clarity and must not be included in the actual response.)
For tarball flakes, the value of the `lastModified` flake attribute is
defined as the timestamp of the newest file inside the tarball.

View file

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

View file

@ -1,6 +1,8 @@
# Release X.Y (202?-??-??) # Release X.Y (202?-??-??)
- Speed-up of downloads from binary caches. - [`nix-channel`](../command-ref/nix-channel.md) now supports a `--list-generations` subcommand
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 function [`builtins.fetchClosure`](../language/builtins.md#builtins-fetchClosure) can now fetch input-addressed paths in [pure evaluation mode](../command-ref/conf-file.md#conf-pure-eval), as those are not impure.
The number of parallel downloads is now set to 16 by default (previously, the default was 1 due to the coupling to build jobs).
- Nix now allows unprivileged/[`allowed-users`](../command-ref/conf-file.md#conf-allowed-users) to sign paths.
Previously, only [`trusted-users`](../command-ref/conf-file.md#conf-trusted-users) users could sign paths.

View file

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

View file

@ -590,6 +590,8 @@
tests.sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/sourcehut-flakes.nix; tests.sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/sourcehut-flakes.nix;
tests.tarballFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/tarball-flakes.nix;
tests.containers = runNixOSTestFor "x86_64-linux" ./tests/nixos/containers/containers.nix; tests.containers = runNixOSTestFor "x86_64-linux" ./tests/nixos/containers/containers.nix;
tests.setuid = lib.genAttrs tests.setuid = lib.genAttrs

View file

@ -117,6 +117,7 @@ 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.
If significant changes are requested or reviewers cannot come to a conclusion in reasonable time, the pull request is [marked as draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#converting-a-pull-request-to-a-draft).
### Assigned ### Assigned

View file

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

View file

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

View file

@ -10,6 +10,7 @@ ConditionPathIsReadWrite=@localstatedir@/nix/daemon-socket
ExecStart=@@bindir@/nix-daemon nix-daemon --daemon ExecStart=@@bindir@/nix-daemon nix-daemon --daemon
KillMode=process KillMode=process
LimitNOFILE=1048576 LimitNOFILE=1048576
TasksMax=1048576
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -100,7 +100,7 @@ poly_extra_try_me_commands() {
poly_configure_nix_daemon_service() { poly_configure_nix_daemon_service() {
task "Setting up the nix-daemon LaunchDaemon" task "Setting up the nix-daemon LaunchDaemon"
_sudo "to set up the nix-daemon as a LaunchDaemon" \ _sudo "to set up the nix-daemon as a LaunchDaemon" \
/bin/cp -f "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST" /usr/bin/install -m -rw-r--r-- "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST"
_sudo "to load the LaunchDaemon plist for nix-daemon" \ _sudo "to load the LaunchDaemon plist for nix-daemon" \
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist

View file

@ -246,8 +246,15 @@ printf -v _OLD_LINE_FMT "%b" $'\033[1;7;31m-'"$ESC ${RED}%L${ESC}"
printf -v _NEW_LINE_FMT "%b" $'\033[1;7;32m+'"$ESC ${GREEN}%L${ESC}" printf -v _NEW_LINE_FMT "%b" $'\033[1;7;32m+'"$ESC ${GREEN}%L${ESC}"
_diff() { _diff() {
# macOS Ventura doesn't ship with GNU diff. Print similar output except
# without +/- markers or dimming
if diff --version | grep -q "Apple diff"; then
printf -v CHANGED_GROUP_FORMAT "%b" "${GREEN}%>${RED}%<${ESC}"
diff --changed-group-format="$CHANGED_GROUP_FORMAT" "$@"
else
# simple colorized diff comatible w/ pre `--color` versions # simple colorized diff comatible w/ pre `--color` versions
diff --unchanged-group-format="$_UNCHANGED_GRP_FMT" --old-line-format="$_OLD_LINE_FMT" --new-line-format="$_NEW_LINE_FMT" --unchanged-line-format=" %L" "$@" diff --unchanged-group-format="$_UNCHANGED_GRP_FMT" --old-line-format="$_OLD_LINE_FMT" --new-line-format="$_NEW_LINE_FMT" --unchanged-line-format=" %L" "$@"
fi
} }
confirm_rm() { confirm_rm() {
@ -693,6 +700,10 @@ EOF
} }
welcome_to_nix() { welcome_to_nix() {
local -r NIX_UID_RANGES="${NIX_FIRST_BUILD_UID}..$((NIX_FIRST_BUILD_UID + NIX_USER_COUNT - 1))"
local -r RANGE_TEXT=$(echo -ne "${BLUE}(uids [${NIX_UID_RANGES}])${ESC}")
local -r GROUP_TEXT=$(echo -ne "${BLUE}(gid ${NIX_BUILD_GROUP_ID})${ESC}")
ok "Welcome to the Multi-User Nix Installation" ok "Welcome to the Multi-User Nix Installation"
cat <<EOF cat <<EOF
@ -706,8 +717,8 @@ manager. This will happen in a few stages:
2. Show you what I am going to install and where. Then I will ask 2. Show you what I am going to install and where. Then I will ask
if you are ready to continue. if you are ready to continue.
3. Create the system users and groups that the Nix daemon uses to run 3. Create the system users ${RANGE_TEXT} and groups ${GROUP_TEXT}
builds. that the Nix daemon uses to run builds.
4. Perform the basic installation of the Nix files daemon. 4. Perform the basic installation of the Nix files daemon.
@ -873,7 +884,7 @@ configure_shell_profile() {
fi fi
done done
task "Setting up shell profiles for Fish with with ${PROFILE_FISH_SUFFIX} inside ${PROFILE_FISH_PREFIXES[*]}" task "Setting up shell profiles for Fish with ${PROFILE_FISH_SUFFIX} inside ${PROFILE_FISH_PREFIXES[*]}"
for fish_prefix in "${PROFILE_FISH_PREFIXES[@]}"; do for fish_prefix in "${PROFILE_FISH_PREFIXES[@]}"; do
if [ ! -d "$fish_prefix" ]; then if [ ! -d "$fish_prefix" ]; then
# this specific prefix (ie: /etc/fish) is very likely to exist # this specific prefix (ie: /etc/fish) is very likely to exist

View file

@ -299,7 +299,7 @@ connected:
!trusted || *trusted; !trusted || *trusted;
}); });
// See the very large comment in `case wopBuildDerivation:` in // See the very large comment in `case WorkerProto::Op::BuildDerivation:` in
// `src/libstore/daemon.cc` that explains the trust model here. // `src/libstore/daemon.cc` that explains the trust model here.
// //
// This condition mirrors that: that code enforces the "rules" outlined there; // This condition mirrors that: that code enforces the "rules" outlined there;

View file

@ -239,9 +239,7 @@ void MixProfile::updateProfile(const StorePath & storePath)
if (!store) throw Error("'--profile' is not supported for this Nix store"); if (!store) throw Error("'--profile' is not supported for this Nix store");
auto profile2 = absPath(*profile); auto profile2 = absPath(*profile);
switchLink(profile2, switchLink(profile2,
createGeneration( createGeneration(*store, profile2, storePath));
ref<LocalFSStore>(store),
profile2, storePath));
} }
void MixProfile::updateProfile(const BuiltPaths & buildables) void MixProfile::updateProfile(const BuiltPaths & buildables)

View file

@ -105,7 +105,9 @@ MixEvalArgs::MixEvalArgs()
)", )",
.category = category, .category = category,
.labels = {"path"}, .labels = {"path"},
.handler = {[&](std::string s) { searchPath.push_back(s); }} .handler = {[&](std::string s) {
searchPath.elements.emplace_back(SearchPath::Elem::parse(s));
}}
}); });
addFlag({ addFlag({
@ -165,7 +167,7 @@ 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).tree.storePath;
return state.rootPath(CanonPath(state.store->toRealPath(storePath))); return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
} }

View file

@ -3,6 +3,7 @@
#include "args.hh" #include "args.hh"
#include "common-args.hh" #include "common-args.hh"
#include "search-path.hh"
namespace nix { namespace nix {
@ -19,7 +20,7 @@ struct MixEvalArgs : virtual Args, virtual MixRepair
Bindings * getAutoArgs(EvalState & state); Bindings * getAutoArgs(EvalState & state);
Strings searchPath; SearchPath searchPath;
std::optional<std::string> evalStoreUrl; std::optional<std::string> evalStoreUrl;

View file

@ -151,7 +151,7 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
}, },
ExtraPathInfoFlake::Flake { ExtraPathInfoFlake::Flake {
.originalRef = flakeRef, .originalRef = flakeRef,
.resolvedRef = getLockedFlake()->flake.lockedRef, .lockedRef = getLockedFlake()->flake.lockedRef,
}), }),
}}; }};
} }

View file

@ -19,7 +19,7 @@ struct ExtraPathInfoFlake : ExtraPathInfoValue
*/ */
struct Flake { struct Flake {
FlakeRef originalRef; FlakeRef originalRef;
FlakeRef resolvedRef; FlakeRef lockedRef;
}; };
Flake flake; Flake flake;

View file

@ -701,7 +701,7 @@ RawInstallablesCommand::RawInstallablesCommand()
{ {
addFlag({ addFlag({
.longName = "stdin", .longName = "stdin",
.description = "Read installables from the standard input.", .description = "Read installables from the standard input. No default installable applied.",
.handler = {&readFromStdIn, true} .handler = {&readFromStdIn, true}
}); });
@ -730,9 +730,9 @@ void RawInstallablesCommand::run(ref<Store> store)
while (std::cin >> word) { while (std::cin >> word) {
rawInstallables.emplace_back(std::move(word)); rawInstallables.emplace_back(std::move(word));
} }
} else {
applyDefaultInstallables(rawInstallables);
} }
applyDefaultInstallables(rawInstallables);
run(store, std::move(rawInstallables)); run(store, std::move(rawInstallables));
} }

View file

@ -68,7 +68,7 @@ struct NixRepl
const Path historyFile; const Path historyFile;
NixRepl(const Strings & searchPath, nix::ref<Store> store,ref<EvalState> state, NixRepl(const SearchPath & searchPath, nix::ref<Store> store,ref<EvalState> state,
std::function<AnnotatedValues()> getValues); std::function<AnnotatedValues()> getValues);
virtual ~NixRepl(); virtual ~NixRepl();
@ -104,7 +104,7 @@ std::string removeWhitespace(std::string s)
} }
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state, NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<NixRepl::AnnotatedValues()> getValues) std::function<NixRepl::AnnotatedValues()> getValues)
: AbstractNixRepl(state) : AbstractNixRepl(state)
, debugTraceIndex(0) , debugTraceIndex(0)
@ -1024,7 +1024,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create( std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state, const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues) std::function<AnnotatedValues()> getValues)
{ {
return std::make_unique<NixRepl>( return std::make_unique<NixRepl>(
@ -1044,7 +1044,7 @@ void AbstractNixRepl::runSimple(
NixRepl::AnnotatedValues values; NixRepl::AnnotatedValues values;
return values; return values;
}; };
const Strings & searchPath = {}; SearchPath searchPath = {};
auto repl = std::make_unique<NixRepl>( auto repl = std::make_unique<NixRepl>(
searchPath, searchPath,
openStore(), openStore(),

View file

@ -25,7 +25,7 @@ struct AbstractNixRepl
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues; typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
static std::unique_ptr<AbstractNixRepl> create( static std::unique_ptr<AbstractNixRepl> create(
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state, const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues); std::function<AnnotatedValues()> getValues);
static void runSimple( static void runSimple(

View file

@ -4,6 +4,7 @@
#include "util.hh" #include "util.hh"
#include "store-api.hh" #include "store-api.hh"
#include "derivations.hh" #include "derivations.hh"
#include "downstream-placeholder.hh"
#include "globals.hh" #include "globals.hh"
#include "eval-inline.hh" #include "eval-inline.hh"
#include "filetransfer.hh" #include "filetransfer.hh"
@ -94,11 +95,16 @@ 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, int depth) const
{ {
checkInterrupt(); checkInterrupt();
if (depth <= 0) {
str << "«too deep»";
return;
}
switch (internalType) { switch (internalType) {
case tInt: case tInt:
str << integer; str << integer;
@ -122,7 +128,7 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
str << "{ "; str << "{ ";
for (auto & i : attrs->lexicographicOrder(symbols)) { for (auto & i : attrs->lexicographicOrder(symbols)) {
str << symbols[i->name] << " = "; str << symbols[i->name] << " = ";
i->value->print(symbols, str, seen); i->value->print(symbols, str, seen, depth - 1);
str << "; "; str << "; ";
} }
str << "}"; str << "}";
@ -138,7 +144,7 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
str << "[ "; str << "[ ";
for (auto v2 : listItems()) { for (auto v2 : listItems()) {
if (v2) if (v2)
v2->print(symbols, str, seen); v2->print(symbols, str, seen, depth - 1);
else else
str << "(nullptr)"; str << "(nullptr)";
str << " "; str << " ";
@ -180,11 +186,10 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
} }
} }
void Value::print(const SymbolTable &symbols, std::ostream &str,
void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const bool showRepeated, int depth) const {
{
std::set<const void *> seen; std::set<const void *> seen;
print(symbols, str, showRepeated ? nullptr : &seen); print(symbols, str, showRepeated ? nullptr : &seen, depth);
} }
// Pretty print types for assertion errors // Pretty print types for assertion errors
@ -210,20 +215,21 @@ const Value * getPrimOp(const Value &v) {
return primOp; return primOp;
} }
std::string_view showType(ValueType type) std::string_view showType(ValueType type, bool withArticle)
{ {
#define WA(a, w) withArticle ? a " " w : w
switch (type) { switch (type) {
case nInt: return "an integer"; case nInt: return WA("an", "integer");
case nBool: return "a Boolean"; case nBool: return WA("a", "Boolean");
case nString: return "a string"; case nString: return WA("a", "string");
case nPath: return "a path"; case nPath: return WA("a", "path");
case nNull: return "null"; case nNull: return "null";
case nAttrs: return "a set"; case nAttrs: return WA("a", "set");
case nList: return "a list"; case nList: return WA("a", "list");
case nFunction: return "a function"; case nFunction: return WA("a", "function");
case nExternal: return "an external value"; case nExternal: return WA("an", "external value");
case nFloat: return "a float"; case nFloat: return WA("a", "float");
case nThunk: return "a thunk"; case nThunk: return WA("a", "thunk");
} }
abort(); abort();
} }
@ -492,7 +498,7 @@ ErrorBuilder & ErrorBuilder::withFrame(const Env & env, const Expr & expr)
EvalState::EvalState( EvalState::EvalState(
const Strings & _searchPath, const SearchPath & _searchPath,
ref<Store> store, ref<Store> store,
std::shared_ptr<Store> buildStore) std::shared_ptr<Store> buildStore)
: sWith(symbols.create("<with>")) : sWith(symbols.create("<with>"))
@ -557,30 +563,32 @@ EvalState::EvalState(
/* Initialise the Nix expression search path. */ /* Initialise the Nix expression search path. */
if (!evalSettings.pureEval) { if (!evalSettings.pureEval) {
for (auto & i : _searchPath) addToSearchPath(i); for (auto & i : _searchPath.elements)
for (auto & i : evalSettings.nixPath.get()) addToSearchPath(i); addToSearchPath(SearchPath::Elem {i});
for (auto & i : evalSettings.nixPath.get())
addToSearchPath(SearchPath::Elem::parse(i));
} }
if (evalSettings.restrictEval || evalSettings.pureEval) { if (evalSettings.restrictEval || evalSettings.pureEval) {
allowedPaths = PathSet(); allowedPaths = PathSet();
for (auto & i : searchPath) { for (auto & i : searchPath.elements) {
auto r = resolveSearchPathElem(i); auto r = resolveSearchPathPath(i.path);
if (!r.first) continue; if (!r) continue;
auto path = r.second; auto path = *std::move(r);
if (store->isInStore(r.second)) { if (store->isInStore(path)) {
try { try {
StorePathSet closure; StorePathSet closure;
store->computeFSClosure(store->toStorePath(r.second).first, closure); store->computeFSClosure(store->toStorePath(path).first, closure);
for (auto & path : closure) for (auto & path : closure)
allowPath(path); allowPath(path);
} catch (InvalidPath &) { } catch (InvalidPath &) {
allowPath(r.second); allowPath(path);
} }
} else } else
allowPath(r.second); allowPath(path);
} }
} }
@ -701,28 +709,34 @@ Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
} }
Value * EvalState::addConstant(const std::string & name, Value & v) Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
{ {
Value * v2 = allocValue(); Value * v2 = allocValue();
*v2 = v; *v2 = v;
addConstant(name, v2); addConstant(name, v2, info);
return v2; return v2;
} }
void EvalState::addConstant(const std::string & name, Value * v) void EvalState::addConstant(const std::string & name, Value * v, Constant info)
{ {
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name; auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
}
constantInfos.push_back({name2, info});
Value * EvalState::addPrimOp(const std::string & name, if (!(evalSettings.pureEval && info.impureOnly)) {
size_t arity, PrimOpFun primOp) /* Check the type, if possible.
{
return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = name }); We might know the type of a thunk in advance, so be allowed
to just write it down in that case. */
if (auto gotType = v->type(true); gotType != nThunk)
assert(info.type == gotType);
/* Install value the base environment. */
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
}
} }
@ -736,7 +750,10 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
vPrimOp->mkPrimOp(new PrimOp(primOp)); vPrimOp->mkPrimOp(new PrimOp(primOp));
Value v; Value v;
v.mkApp(vPrimOp, vPrimOp); v.mkApp(vPrimOp, vPrimOp);
return addConstant(primOp.name, v); return addConstant(primOp.name, v, {
.type = nThunk, // FIXME
.doc = primOp.doc,
});
} }
auto envName = symbols.create(primOp.name); auto envName = symbols.create(primOp.name);
@ -762,13 +779,13 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
{ {
if (v.isPrimOp()) { if (v.isPrimOp()) {
auto v2 = &v; auto v2 = &v;
if (v2->primOp->doc) if (auto * doc = v2->primOp->doc)
return Doc { return Doc {
.pos = {}, .pos = {},
.name = v2->primOp->name, .name = v2->primOp->name,
.arity = v2->primOp->arity, .arity = v2->primOp->arity,
.args = v2->primOp->args, .args = v2->primOp->args,
.doc = v2->primOp->doc, .doc = doc,
}; };
} }
return {}; return {};
@ -1058,7 +1075,7 @@ void EvalState::mkOutputString(
? store->printStorePath(*std::move(optOutputPath)) ? store->printStorePath(*std::move(optOutputPath))
/* Downstream we would substitute this for an actual path once /* Downstream we would substitute this for an actual path once
we build the floating CA derivation */ we build the floating CA derivation */
: downstreamPlaceholder(*store, drvPath, outputName), : DownstreamPlaceholder::unknownCaOutput(drvPath, outputName).render(),
NixStringContext { NixStringContext {
NixStringContextElem::Built { NixStringContextElem::Built {
.drvPath = drvPath, .drvPath = drvPath,
@ -2380,7 +2397,7 @@ DerivedPath EvalState::coerceToDerivedPath(const PosIdx pos, Value & v, std::str
// This is testing for the case of CA derivations // This is testing for the case of CA derivations
auto sExpected = optOutputPath auto sExpected = optOutputPath
? store->printStorePath(*optOutputPath) ? store->printStorePath(*optOutputPath)
: downstreamPlaceholder(*store, b.drvPath, output); : DownstreamPlaceholder::unknownCaOutput(b.drvPath, output).render();
if (s != sExpected) if (s != sExpected)
error( 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'", "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'",
@ -2619,7 +2636,7 @@ Strings EvalSettings::getDefaultNixPath()
{ {
Strings res; Strings res;
auto add = [&](const Path & p, const std::string & s = std::string()) { auto add = [&](const Path & p, const std::string & s = std::string()) {
if (pathExists(p)) { if (pathAccessible(p)) {
if (s.empty()) { if (s.empty()) {
res.push_back(p); res.push_back(p);
} else { } else {

View file

@ -9,6 +9,7 @@
#include "config.hh" #include "config.hh"
#include "experimental-features.hh" #include "experimental-features.hh"
#include "input-accessor.hh" #include "input-accessor.hh"
#include "search-path.hh"
#include <map> #include <map>
#include <optional> #include <optional>
@ -25,15 +26,72 @@ struct DerivedPath;
enum RepairFlag : bool; enum RepairFlag : bool;
/**
* Function that implements a primop.
*/
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v); typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
/**
* Info about a primitive operation, and its implementation
*/
struct PrimOp struct PrimOp
{ {
PrimOpFun fun; /**
size_t arity; * Name of the primop. `__` prefix is treated specially.
*/
std::string name; std::string name;
/**
* Names of the parameters of a primop, for primops that take a
* fixed number of arguments to be substituted for these parameters.
*/
std::vector<std::string> args; std::vector<std::string> args;
/**
* Aritiy of the primop.
*
* If `args` is not empty, this field will be computed from that
* field instead, so it doesn't need to be manually set.
*/
size_t arity = 0;
/**
* Optional free-form documentation about the primop.
*/
const char * doc = nullptr; const char * doc = nullptr;
/**
* Implementation of the primop.
*/
PrimOpFun fun;
/**
* Optional experimental for this to be gated on.
*/
std::optional<ExperimentalFeature> experimentalFeature;
};
/**
* Info about a constant
*/
struct Constant
{
/**
* Optional type of the constant (known since it is a fixed value).
*
* @todo we should use an enum for this.
*/
ValueType type = nThunk;
/**
* Optional free-form documentation about the constant.
*/
const char * doc = nullptr;
/**
* Whether the constant is impure, and not available in pure mode.
*/
bool impureOnly = false;
}; };
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
@ -65,11 +123,6 @@ 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::list<SearchPathElem> SearchPath;
/** /**
* Initialise the Boehm GC, if applicable. * Initialise the Boehm GC, if applicable.
*/ */
@ -256,7 +309,7 @@ private:
SearchPath searchPath; SearchPath searchPath;
std::map<std::string, std::pair<bool, std::string>> searchPathResolved; std::map<std::string, std::optional<std::string>> searchPathResolved;
/** /**
* Cache used by checkSourcePath(). * Cache used by checkSourcePath().
@ -283,12 +336,12 @@ private:
public: public:
EvalState( EvalState(
const Strings & _searchPath, const SearchPath & _searchPath,
ref<Store> store, ref<Store> store,
std::shared_ptr<Store> buildStore = nullptr); std::shared_ptr<Store> buildStore = nullptr);
~EvalState(); ~EvalState();
void addToSearchPath(const std::string & s); void addToSearchPath(SearchPath::Elem && elem);
SearchPath getSearchPath() { return searchPath; } SearchPath getSearchPath() { return searchPath; }
@ -370,12 +423,16 @@ public:
* Look up a file in the search path. * Look up a file in the search path.
*/ */
SourcePath findFile(const std::string_view path); SourcePath findFile(const std::string_view path);
SourcePath findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos); SourcePath findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/** /**
* Try to resolve a search path value (not the optinal key part)
*
* If the specified search path element is a URI, download it. * If the specified search path element is a URI, download it.
*
* If it is not found, return `std::nullopt`
*/ */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem); std::optional<std::string> resolveSearchPathPath(const SearchPath::Path & path);
/** /**
* Evaluate an expression to normal form * Evaluate an expression to normal form
@ -483,7 +540,7 @@ public:
* Coerce to `DerivedPath`. * Coerce to `DerivedPath`.
* *
* Must be a string which is either a literal store path or a * Must be a string which is either a literal store path or a
* "placeholder (see `downstreamPlaceholder()`). * "placeholder (see `DownstreamPlaceholder`).
* *
* Even more importantly, the string context must be exactly one * Even more importantly, the string context must be exactly one
* element, which is either a `NixStringContextElem::Opaque` or * element, which is either a `NixStringContextElem::Opaque` or
@ -509,18 +566,23 @@ public:
*/ */
std::shared_ptr<StaticEnv> staticBaseEnv; // !!! should be private std::shared_ptr<StaticEnv> staticBaseEnv; // !!! should be private
/**
* Name and documentation about every constant.
*
* Constants from primops are hard to crawl, and their docs will go
* here too.
*/
std::vector<std::pair<std::string, Constant>> constantInfos;
private: private:
unsigned int baseEnvDispl = 0; unsigned int baseEnvDispl = 0;
void createBaseEnv(); void createBaseEnv();
Value * addConstant(const std::string & name, Value & v); Value * addConstant(const std::string & name, Value & v, Constant info);
void addConstant(const std::string & name, Value * v); void addConstant(const std::string & name, Value * v, Constant info);
Value * addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp);
Value * addPrimOp(PrimOp && primOp); Value * addPrimOp(PrimOp && primOp);
@ -534,6 +596,10 @@ public:
std::optional<std::string> name; std::optional<std::string> name;
size_t arity; size_t arity;
std::vector<std::string> args; std::vector<std::string> args;
/**
* Unlike the other `doc` fields in this file, this one should never be
* `null`.
*/
const char * doc; const char * doc;
}; };
@ -622,7 +688,7 @@ public:
* @param optOutputPath Optional output path for that string. Must * @param optOutputPath Optional output path for that string. Must
* be passed if and only if output store object is input-addressed. * 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 printed to form string if passed, otherwise a placeholder
* will be used (see `downstreamPlaceholder()`). * will be used (see `DownstreamPlaceholder`).
*/ */
void mkOutputString( void mkOutputString(
Value & value, Value & value,
@ -700,8 +766,11 @@ struct DebugTraceStacker {
/** /**
* @return A string representing the type of the value `v`. * @return A string representing the type of the value `v`.
*
* @param withArticle Whether to begin with an english article, e.g. "an
* integer" vs "integer".
*/ */
std::string_view showType(ValueType type); std::string_view showType(ValueType type, bool withArticle = true);
std::string showType(const Value & v); std::string showType(const Value & v);
/** /**
@ -733,7 +802,12 @@ struct EvalSettings : Config
Setting<Strings> nixPath{ Setting<Strings> nixPath{
this, getDefaultNixPath(), "nix-path", this, getDefaultNixPath(), "nix-path",
"List of directories to be searched for `<...>` file references."}; R"(
List of directories to be searched for `<...>` file references
In particular, outside of [pure evaluation mode](#conf-pure-evaluation), this determines the value of
[`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtin-constants-nixPath).
)"};
Setting<bool> restrictEval{ Setting<bool> restrictEval{
this, false, "restrict-eval", this, false, "restrict-eval",
@ -741,11 +815,18 @@ struct EvalSettings : Config
If set to `true`, the Nix evaluator will not allow access to any If set to `true`, the Nix evaluator will not allow access to any
files outside of the Nix search path (as set via the `NIX_PATH` files outside of the Nix search path (as set via the `NIX_PATH`
environment variable or the `-I` option), or to URIs outside of environment variable or the `-I` option), or to URIs outside of
`allowed-uri`. The default is `false`. [`allowed-uris`](../command-ref/conf-file.md#conf-allowed-uris).
The default is `false`.
)"}; )"};
Setting<bool> pureEval{this, false, "pure-eval", Setting<bool> pureEval{this, false, "pure-eval",
"Whether to restrict file system and network access to files specified by cryptographic hash."}; R"(
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
- Restrict file system and network access to files specified by cryptographic hash
- Disable [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
)"
};
Setting<bool> enableImportFromDerivation{ Setting<bool> enableImportFromDerivation{
this, true, "allow-import-from-derivation", this, true, "allow-import-from-derivation",

View file

@ -788,9 +788,6 @@ static RegisterPrimOp r2({
```nix ```nix
(builtins.getFlake "github:edolstra/dwarffs").rev (builtins.getFlake "github:edolstra/dwarffs").rev
``` ```
This function is only available if you enable the experimental feature
`flakes`.
)", )",
.fun = prim_getFlake, .fun = prim_getFlake,
.experimentalFeature = Xp::Flakes, .experimentalFeature = Xp::Flakes,

View file

@ -36,7 +36,7 @@ static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
#define CUR_POS makeCurPos(*yylloc, data) #define CUR_POS makeCurPos(*yylloc, data)
// backup to recover from yyless(0) // backup to recover from yyless(0)
YYLTYPE prev_yylloc; thread_local YYLTYPE prev_yylloc;
static void initLoc(YYLTYPE * loc) static void initLoc(YYLTYPE * loc)
{ {

View file

@ -275,7 +275,12 @@ static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
} }
/* If this is a single string, then don't do a concatenation. */ /* If this is a single string, then don't do a concatenation. */
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second) ? (*es2)[0].second : new ExprConcatStrings(pos, true, es2); if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
auto *const result = (*es2)[0].second;
delete es2;
return result;
}
return new ExprConcatStrings(pos, true, es2);
} }
@ -330,7 +335,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%type <ind_string_parts> ind_string_parts %type <ind_string_parts> ind_string_parts
%type <e> path_start string_parts string_attr %type <e> path_start string_parts string_attr
%type <id> attr %type <id> attr
%token <id> ID ATTRPATH %token <id> ID
%token <str> STR IND_STR %token <str> STR IND_STR
%token <n> INT %token <n> INT
%token <nf> FLOAT %token <nf> FLOAT
@ -658,7 +663,7 @@ Expr * EvalState::parse(
ParseData data { ParseData data {
.state = *this, .state = *this,
.symbols = symbols, .symbols = symbols,
.basePath = std::move(basePath), .basePath = basePath,
.origin = {origin}, .origin = {origin},
}; };
@ -729,19 +734,9 @@ Expr * EvalState::parseStdin()
} }
void EvalState::addToSearchPath(const std::string & s) void EvalState::addToSearchPath(SearchPath::Elem && elem)
{ {
size_t pos = s.find('='); searchPath.elements.emplace_back(std::move(elem));
std::string prefix;
Path path;
if (pos == std::string::npos) {
path = s;
} else {
prefix = std::string(s, 0, pos);
path = std::string(s, pos + 1);
}
searchPath.emplace_back(prefix, path);
} }
@ -751,22 +746,19 @@ SourcePath EvalState::findFile(const std::string_view path)
} }
SourcePath EvalState::findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos) SourcePath EvalState::findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos)
{ {
for (auto & i : searchPath) { for (auto & i : searchPath.elements) {
std::string suffix; auto suffixOpt = i.prefix.suffixIfPotentialMatch(path);
if (i.first.empty())
suffix = concatStrings("/", path); if (!suffixOpt) continue;
else { auto suffix = *suffixOpt;
auto s = i.first.size();
if (path.compare(0, s, i.first) != 0 || auto rOpt = resolveSearchPathPath(i.path);
(path.size() > s && path[s] != '/')) if (!rOpt) continue;
continue; auto r = *rOpt;
suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
} Path res = suffix == "" ? r : concatStrings(r, "/", suffix);
auto r = resolveSearchPathElem(i);
if (!r.first) continue;
Path res = r.second + suffix;
if (pathExists(res)) return CanonPath(canonPath(res)); if (pathExists(res)) return CanonPath(canonPath(res));
} }
@ -783,49 +775,53 @@ SourcePath EvalState::findFile(SearchPath & searchPath, const std::string_view p
} }
std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathElem & elem) std::optional<std::string> EvalState::resolveSearchPathPath(const SearchPath::Path & value0)
{ {
auto i = searchPathResolved.find(elem.second); auto & value = value0.s;
auto i = searchPathResolved.find(value);
if (i != searchPathResolved.end()) return i->second; if (i != searchPathResolved.end()) return i->second;
std::pair<bool, std::string> res; std::optional<std::string> res;
if (EvalSettings::isPseudoUrl(elem.second)) { if (EvalSettings::isPseudoUrl(value)) {
try { try {
auto storePath = fetchers::downloadTarball( auto storePath = fetchers::downloadTarball(
store, EvalSettings::resolvePseudoUrl(elem.second), "source", false).first.storePath; store, EvalSettings::resolvePseudoUrl(value), "source", false).tree.storePath;
res = { true, store->toRealPath(storePath) }; res = { store->toRealPath(storePath) };
} catch (FileTransferError & e) { } catch (FileTransferError & e) {
logWarning({ logWarning({
.msg = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second) .msg = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value)
}); });
res = { false, "" }; res = std::nullopt;
} }
} }
else if (hasPrefix(elem.second, "flake:")) { else if (hasPrefix(value, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(elem.second.substr(6), {}, true, false); auto flakeRef = parseFlakeRef(value.substr(6), {}, true, false);
debug("fetching flake search path element '%s''", elem.second); debug("fetching flake search path element '%s''", value);
auto storePath = flakeRef.resolve(store).fetchTree(store).first.storePath; auto storePath = flakeRef.resolve(store).fetchTree(store).first.storePath;
res = { true, store->toRealPath(storePath) }; res = { store->toRealPath(storePath) };
} }
else { else {
auto path = absPath(elem.second); auto path = absPath(value);
if (pathExists(path)) if (pathExists(path))
res = { true, path }; res = { path };
else { else {
logWarning({ logWarning({
.msg = hintfmt("Nix search path entry '%1%' does not exist, ignoring", elem.second) .msg = hintfmt("Nix search path entry '%1%' does not exist, ignoring", value)
}); });
res = { false, "" }; res = std::nullopt;
} }
} }
debug("resolved search path element '%s' to '%s'", elem.second, res.second); if (res)
debug("resolved search path element '%s' to '%s'", value, *res);
else
debug("failed to resolve search path element '%s'", value);
searchPathResolved[elem.second] = res; searchPathResolved[value] = res;
return res; return res;
} }

View file

@ -1,11 +1,12 @@
#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"
#include "json-to-value.hh" #include "json-to-value.hh"
#include "names.hh" #include "names.hh"
#include "references.hh" #include "path-references.hh"
#include "store-api.hh" #include "store-api.hh"
#include "util.hh" #include "util.hh"
#include "value-to-json.hh" #include "value-to-json.hh"
@ -87,7 +88,7 @@ StringMap EvalState::realiseContext(const NixStringContext & 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)
); );
} }
@ -237,7 +238,7 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
} }
} }
static RegisterPrimOp primop_scopedImport(RegisterPrimOp::Info { static RegisterPrimOp primop_scopedImport(PrimOp {
.name = "scopedImport", .name = "scopedImport",
.arity = 2, .arity = 2,
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
@ -691,7 +692,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
v.listElems()[n++] = i; v.listElems()[n++] = i;
} }
static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info { static RegisterPrimOp primop_genericClosure(PrimOp {
.name = "__genericClosure", .name = "__genericClosure",
.args = {"attrset"}, .args = {"attrset"},
.arity = 1, .arity = 1,
@ -808,7 +809,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
} }
} }
static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info { static RegisterPrimOp primop_addErrorContext(PrimOp {
.name = "__addErrorContext", .name = "__addErrorContext",
.arity = 2, .arity = 2,
.fun = prim_addErrorContext, .fun = prim_addErrorContext,
@ -1151,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
@ -1401,7 +1400,7 @@ drvName, Bindings * attrs, Value & v)
v.mkAttrs(result); v.mkAttrs(result);
} }
static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { static RegisterPrimOp primop_derivationStrict(PrimOp {
.name = "derivationStrict", .name = "derivationStrict",
.arity = 1, .arity = 1,
.fun = prim_derivationStrict, .fun = prim_derivationStrict,
@ -1502,7 +1501,9 @@ 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).
See also [`builtins.fetchClosure`](#builtins-fetchClosure).
)", )",
.fun = prim_storePath, .fun = prim_storePath,
}); });
@ -1657,7 +1658,10 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
})); }));
} }
searchPath.emplace_back(prefix, path); searchPath.elements.emplace_back(SearchPath::Elem {
.prefix = SearchPath::Prefix { .s = prefix },
.path = SearchPath::Path { .s = path },
});
} }
auto path = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.findFile"); auto path = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.findFile");
@ -1665,9 +1669,52 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
} }
static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { static RegisterPrimOp primop_findFile(PrimOp {
.name = "__findFile", .name = "__findFile",
.arity = 2, .args = {"search path", "lookup path"},
.doc = R"(
Look up the given path with the given search path.
A search path is represented list of [attribute sets](./values.md#attribute-set) with two attributes, `prefix`, and `path`.
`prefix` is a relative path.
`path` denotes a file system location; the exact syntax depends on the command line interface.
Examples of search path attribute sets:
- ```
{
prefix = "nixos-config";
path = "/etc/nixos/configuration.nix";
}
```
- ```
{
prefix = "";
path = "/nix/var/nix/profiles/per-user/root/channels";
}
```
The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/values.html#type-path) of the match.
This is the process for each entry:
If the lookup path matches `prefix`, then the remainder of the lookup path (the "suffix") is searched for within the directory denoted by `patch`.
Note that the `path` may need to be downloaded at this point to look inside.
If the suffix is found inside that directory, then the entry is a match;
the combined absolute path of the directory (now downloaded if need be) and the suffix is returned.
The syntax
```nix
<nixpkgs>
```
is equivalent to:
```nix
builtins.findFile builtins.nixPath "nixpkgs"
```
)",
.fun = prim_findFile, .fun = prim_findFile,
}); });
@ -2386,7 +2433,7 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * *
state.mkPos(v, i->pos); state.mkPos(v, i->pos);
} }
static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info { static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp {
.name = "__unsafeGetAttrPos", .name = "__unsafeGetAttrPos",
.arity = 2, .arity = 2,
.fun = prim_unsafeGetAttrPos, .fun = prim_unsafeGetAttrPos,
@ -3909,13 +3956,8 @@ 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, NixStringContext>> 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()) {
NixStringContext 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));
}
NixStringContext 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");
@ -3926,10 +3968,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];
@ -3937,9 +3988,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) {
@ -3957,7 +4005,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"
@ -4054,22 +4106,10 @@ static RegisterPrimOp primop_splitVersion({
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps; RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun) RegisterPrimOp::RegisterPrimOp(PrimOp && primOp)
{ {
if (!primOps) primOps = new PrimOps; if (!primOps) primOps = new PrimOps;
primOps->push_back({ primOps->push_back(std::move(primOp));
.name = name,
.args = {},
.arity = arity,
.fun = fun,
});
}
RegisterPrimOp::RegisterPrimOp(Info && info)
{
if (!primOps) primOps = new PrimOps;
primOps->push_back(std::move(info));
} }
@ -4082,85 +4122,253 @@ void EvalState::createBaseEnv()
/* `builtins' must be first! */ /* `builtins' must be first! */
v.mkAttrs(buildBindings(128).finish()); v.mkAttrs(buildBindings(128).finish());
addConstant("builtins", v); addConstant("builtins", v, {
.type = nAttrs,
.doc = R"(
Contains all the [built-in functions](@docroot@/language/builtins.md) and values.
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:
```nix
# if hasContext is not available, we assume `s` has a context
if builtins ? hasContext then builtins.hasContext s else true
```
)",
});
v.mkBool(true); v.mkBool(true);
addConstant("true", v); addConstant("true", v, {
.type = nBool,
.doc = R"(
Primitive value.
It can be returned by
[comparison operators](@docroot@/language/operators.md#Comparison)
and used in
[conditional expressions](@docroot@/language/constructs.md#Conditionals).
The name `true` is not special, and can be shadowed:
```nix-repl
nix-repl> let true = 1; in true
1
```
)",
});
v.mkBool(false); v.mkBool(false);
addConstant("false", v); addConstant("false", v, {
.type = nBool,
.doc = R"(
Primitive value.
It can be returned by
[comparison operators](@docroot@/language/operators.md#Comparison)
and used in
[conditional expressions](@docroot@/language/constructs.md#Conditionals).
The name `false` is not special, and can be shadowed:
```nix-repl
nix-repl> let false = 1; in false
1
```
)",
});
v.mkNull(); v.mkNull();
addConstant("null", v); addConstant("null", v, {
.type = nNull,
.doc = R"(
Primitive value.
The name `null` is not special, and can be shadowed:
```nix-repl
nix-repl> let null = 1; in null
1
```
)",
});
if (!evalSettings.pureEval) { if (!evalSettings.pureEval) {
v.mkInt(time(0)); v.mkInt(time(0));
addConstant("__currentTime", v);
v.mkString(settings.thisSystem.get());
addConstant("__currentSystem", v);
} }
addConstant("__currentTime", v, {
.type = nInt,
.doc = R"(
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, unless both evaluate `builtins.currentTime` in the same second.
)",
.impureOnly = true,
});
if (!evalSettings.pureEval) {
v.mkString(settings.thisSystem.get());
}
addConstant("__currentSystem", v, {
.type = nString,
.doc = R"(
The value of the [`system` configuration option](@docroot@/command-ref/conf-file.md#conf-pure-eval).
It can be used to set the `system` attribute for [`builtins.derivation`](@docroot@/language/derivations.md) such that the resulting derivation can be built on the same system that evaluates the Nix expression:
```nix
builtins.derivation {
# ...
system = builtins.currentSystem;
}
```
It can be overridden in order to create derivations for different system than the current one:
```console
$ nix-instantiate --system "mips64-linux" --eval --expr 'builtins.currentSystem'
"mips64-linux"
```
)",
.impureOnly = true,
});
v.mkString(nixVersion); v.mkString(nixVersion);
addConstant("__nixVersion", v); addConstant("__nixVersion", v, {
.type = nString,
.doc = R"(
The version of Nix.
For example, where the command line returns the current Nix version,
```shell-session
$ nix --version
nix (Nix) 2.16.0
```
the Nix language evaluator returns the same value:
```nix-repl
nix-repl> builtins.nixVersion
"2.16.0"
```
)",
});
v.mkString(store->storeDir); v.mkString(store->storeDir);
addConstant("__storeDir", v); addConstant("__storeDir", v, {
.type = nString,
.doc = R"(
Logical file system location of the [Nix store](@docroot@/glossary.md#gloss-store) currently in use.
This value is determined by the `store` parameter in [Store URLs](@docroot@/command-ref/new-cli/nix3-help-stores.md):
```shell-session
$ nix-instantiate --store 'dummy://?store=/blah' --eval --expr builtins.storeDir
"/blah"
```
)",
});
/* Language version. This should be increased every time a new /* Language version. This should be increased every time a new
language feature gets added. It's not necessary to increase it language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ? when primops get added, because you can just use `builtins ?
primOp' to check. */ primOp' to check. */
v.mkInt(6); v.mkInt(6);
addConstant("__langVersion", v); addConstant("__langVersion", v, {
.type = nInt,
.doc = R"(
The current version of the Nix language.
)",
});
// Miscellaneous // Miscellaneous
if (evalSettings.enableNativeCode) { if (evalSettings.enableNativeCode) {
addPrimOp("__importNative", 2, prim_importNative); addPrimOp({
addPrimOp("__exec", 1, prim_exec); .name = "__importNative",
.arity = 2,
.fun = prim_importNative,
});
addPrimOp({
.name = "__exec",
.arity = 1,
.fun = prim_exec,
});
} }
addPrimOp({ addPrimOp({
.fun = evalSettings.traceVerbose ? prim_trace : prim_second,
.arity = 2,
.name = "__traceVerbose", .name = "__traceVerbose",
.args = { "e1", "e2" }, .args = { "e1", "e2" },
.arity = 2,
.doc = R"( .doc = R"(
Evaluate *e1* and print its abstract syntax representation on standard Evaluate *e1* and print its abstract syntax representation on standard
error if `--trace-verbose` is enabled. Then return *e2*. This function error if `--trace-verbose` is enabled. Then return *e2*. This function
is useful for debugging. is useful for debugging.
)", )",
.fun = evalSettings.traceVerbose ? prim_trace : prim_second,
}); });
/* Add a value containing the current Nix expression search path. */ /* Add a value containing the current Nix expression search path. */
mkList(v, searchPath.size()); mkList(v, searchPath.elements.size());
int n = 0; int n = 0;
for (auto & i : searchPath) { for (auto & i : searchPath.elements) {
auto attrs = buildBindings(2); auto attrs = buildBindings(2);
attrs.alloc("path").mkString(i.second); attrs.alloc("path").mkString(i.path.s);
attrs.alloc("prefix").mkString(i.first); attrs.alloc("prefix").mkString(i.prefix.s);
(v.listElems()[n++] = allocValue())->mkAttrs(attrs); (v.listElems()[n++] = allocValue())->mkAttrs(attrs);
} }
addConstant("__nixPath", v); addConstant("__nixPath", v, {
.type = nList,
.doc = R"(
The search path used to resolve angle bracket path lookups.
Angle bracket expressions can be
[desugared](https://en.wikipedia.org/wiki/Syntactic_sugar)
using this and
[`builtins.findFile`](./builtins.html#builtins-findFile):
```nix
<nixpkgs>
```
is equivalent to:
```nix
builtins.findFile builtins.nixPath "nixpkgs"
```
)",
});
if (RegisterPrimOp::primOps) if (RegisterPrimOp::primOps)
for (auto & primOp : *RegisterPrimOp::primOps) for (auto & primOp : *RegisterPrimOp::primOps)
if (!primOp.experimentalFeature if (experimentalFeatureSettings.isEnabled(primOp.experimentalFeature))
|| experimentalFeatureSettings.isEnabled(*primOp.experimentalFeature))
{ {
addPrimOp({ auto primOpAdjusted = primOp;
.fun = primOp.fun, primOpAdjusted.arity = std::max(primOp.args.size(), primOp.arity);
.arity = std::max(primOp.args.size(), primOp.arity), addPrimOp(std::move(primOpAdjusted));
.name = primOp.name,
.args = primOp.args,
.doc = primOp.doc,
});
} }
/* 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.
Null docs because it is documented separately.
*/
auto vDerivation = allocValue(); auto vDerivation = allocValue();
addConstant("derivation", vDerivation); addConstant("derivation", vDerivation, {
.type = nFunction,
});
/* Now that we've added all primops, sort the `builtins' set, /* Now that we've added all primops, sort the `builtins' set,
because attribute lookups expect it to be sorted. */ because attribute lookups expect it to be sorted. */

View file

@ -10,17 +10,7 @@ namespace nix {
struct RegisterPrimOp struct RegisterPrimOp
{ {
struct Info typedef std::vector<PrimOp> PrimOps;
{
std::string name;
std::vector<std::string> args;
size_t arity = 0;
const char * doc;
PrimOpFun fun;
std::optional<ExperimentalFeature> experimentalFeature;
};
typedef std::vector<Info> PrimOps;
static PrimOps * primOps; static PrimOps * primOps;
/** /**
@ -28,12 +18,7 @@ struct RegisterPrimOp
* will get called during EvalState initialization, so there * will get called during EvalState initialization, so there
* may be primops not yet added and builtins is not yet sorted. * may be primops not yet added and builtins is not yet sorted.
*/ */
RegisterPrimOp( RegisterPrimOp(PrimOp && primOp);
std::string name,
size_t arity,
PrimOpFun fun);
RegisterPrimOp(Info && info);
}; };
/* These primops are disabled without enableNativeCode, but plugins /* These primops are disabled without enableNativeCode, but plugins

View file

@ -12,7 +12,11 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos,
v.mkString(*s); v.mkString(*s);
} }
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); static RegisterPrimOp primop_unsafeDiscardStringContext({
.name = "__unsafeDiscardStringContext",
.arity = 1,
.fun = prim_unsafeDiscardStringContext
});
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)
@ -22,7 +26,16 @@ static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args,
v.mkBool(!context.empty()); v.mkBool(!context.empty());
} }
static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext); static RegisterPrimOp primop_hasContext({
.name = "__hasContext",
.args = {"s"},
.doc = R"(
Return `true` if string *s* has a non-empty context. The
context can be obtained with
[`getContext`](#builtins-getContext).
)",
.fun = prim_hasContext
});
/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a /* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
@ -51,7 +64,11 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
v.mkString(*s, context2); v.mkString(*s, context2);
} }
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); static RegisterPrimOp primop_unsafeDiscardOutputDependency({
.name = "__unsafeDiscardOutputDependency",
.arity = 1,
.fun = prim_unsafeDiscardOutputDependency
});
/* Extract the context of a string as a structured Nix value. /* Extract the context of a string as a structured Nix value.
@ -119,7 +136,30 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
v.mkAttrs(attrs); v.mkAttrs(attrs);
} }
static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext); static RegisterPrimOp primop_getContext({
.name = "__getContext",
.args = {"s"},
.doc = R"(
Return the string context of *s*.
The string context tracks references to derivations within a string.
It is represented as an attribute set of [store derivation](@docroot@/glossary.md#gloss-store-derivation) paths mapping to output names.
Using [string interpolation](@docroot@/language/string-interpolation.md) on a derivation will add that derivation to the string context.
For example,
```nix
builtins.getContext "${derivation { name = "a"; builder = "b"; system = "c"; }}"
```
evaluates to
```
{ "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; }
```
)",
.fun = prim_getContext
});
/* Append the given context to a given string. /* Append the given context to a given string.
@ -192,6 +232,10 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
v.mkString(orig, context); v.mkString(orig, context);
} }
static RegisterPrimOp primop_appendContext("__appendContext", 2, prim_appendContext); static RegisterPrimOp primop_appendContext({
.name = "__appendContext",
.arity = 2,
.fun = prim_appendContext
});
} }

View file

@ -5,37 +5,150 @@
namespace nix { namespace nix {
/**
* Handler for the content addressed case.
*
* @param state Evaluator state and store to write to.
* @param fromStore Store containing the path to rewrite.
* @param fromPath Source path to be rewritten.
* @param toPathMaybe Path to write the rewritten path to. If empty, the error shows the actual path.
* @param v Return `Value`
*/
static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, const std::optional<StorePath> & toPathMaybe, Value &v) {
// establish toPath or throw
if (!toPathMaybe || !state.store->isValidPath(*toPathMaybe)) {
auto rewrittenPath = makeContentAddressed(fromStore, *state.store, fromPath);
if (toPathMaybe && *toPathMaybe != rewrittenPath)
throw Error({
.msg = hintfmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
state.store->printStorePath(fromPath),
state.store->printStorePath(rewrittenPath),
state.store->printStorePath(*toPathMaybe)),
.errPos = state.positions[pos]
});
if (!toPathMaybe)
throw Error({
.msg = hintfmt(
"rewriting '%s' to content-addressed form yielded '%s'\n"
"Use this value for the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(fromPath),
state.store->printStorePath(rewrittenPath)),
.errPos = state.positions[pos]
});
}
auto toPath = *toPathMaybe;
// check and return
auto resultInfo = state.store->queryPathInfo(toPath);
if (!resultInfo->isContentAddressed(*state.store)) {
// We don't perform the rewriting when outPath already exists, as an optimisation.
// However, we can quickly detect a mistake if the toPath is input addressed.
throw Error({
.msg = hintfmt(
"The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n"
"Set 'toPath' to an empty string to make Nix report the correct content-addressed path.",
state.store->printStorePath(toPath)),
.errPos = state.positions[pos]
});
}
state.mkStorePathString(toPath, v);
}
/**
* Fetch the closure and make sure it's content addressed.
*/
static void runFetchClosureWithContentAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) {
if (!state.store->isValidPath(fromPath))
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath });
auto info = state.store->queryPathInfo(fromPath);
if (!info->isContentAddressed(*state.store)) {
throw Error({
.msg = hintfmt(
"The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n"
"If you do intend to fetch an input-addressed store path, add\n\n"
" inputAddressed = true;\n\n"
"to the 'fetchClosure' arguments.\n\n"
"Note that to ensure authenticity input-addressed store paths, users must configure a trusted binary cache public key on their systems. This is not needed for content-addressed paths.",
state.store->printStorePath(fromPath)),
.errPos = state.positions[pos]
});
}
state.mkStorePathString(fromPath, v);
}
/**
* Fetch the closure and make sure it's input addressed.
*/
static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) {
if (!state.store->isValidPath(fromPath))
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath });
auto info = state.store->queryPathInfo(fromPath);
if (info->isContentAddressed(*state.store)) {
throw Error({
.msg = hintfmt(
"The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n"
"Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed",
state.store->printStorePath(fromPath)),
.errPos = state.positions[pos]
});
}
state.mkStorePathString(fromPath, v);
}
typedef std::optional<StorePath> StorePathOrGap;
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure"); state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure");
std::optional<std::string> fromStoreUrl; std::optional<std::string> fromStoreUrl;
std::optional<StorePath> fromPath; std::optional<StorePath> fromPath;
bool toCA = false; std::optional<StorePathOrGap> toPath;
std::optional<StorePath> toPath; std::optional<bool> inputAddressedMaybe;
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
const auto & attrName = state.symbols[attr.name]; const auto & attrName = state.symbols[attr.name];
auto attrHint = [&]() -> std::string {
return "while evaluating the '" + attrName + "' attribute passed to builtins.fetchClosure";
};
if (attrName == "fromPath") { if (attrName == "fromPath") {
NixStringContext context; NixStringContext context;
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context, fromPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
"while evaluating the 'fromPath' attribute passed to builtins.fetchClosure");
} }
else if (attrName == "toPath") { else if (attrName == "toPath") {
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
toCA = true; bool isEmptyString = attr.value->type() == nString && attr.value->string.s == std::string("");
if (attr.value->type() != nString || attr.value->string.s != std::string("")) { if (isEmptyString) {
toPath = StorePathOrGap {};
}
else {
NixStringContext context; NixStringContext context;
toPath = state.coerceToStorePath(attr.pos, *attr.value, context, toPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
"while evaluating the 'toPath' attribute passed to builtins.fetchClosure");
} }
} }
else if (attrName == "fromStore") else if (attrName == "fromStore")
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos, fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos,
"while evaluating the 'fromStore' attribute passed to builtins.fetchClosure"); attrHint());
else if (attrName == "inputAddressed")
inputAddressedMaybe = state.forceBool(*attr.value, attr.pos, attrHint());
else else
throw Error({ throw Error({
@ -50,6 +163,18 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
.errPos = state.positions[pos] .errPos = state.positions[pos]
}); });
bool inputAddressed = inputAddressedMaybe.value_or(false);
if (inputAddressed) {
if (toPath)
throw Error({
.msg = hintfmt("attribute '%s' is set to true, but '%s' is also set. Please remove one of them",
"inputAddressed",
"toPath"),
.errPos = state.positions[pos]
});
}
if (!fromStoreUrl) if (!fromStoreUrl)
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"), .msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
@ -74,55 +199,40 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
auto fromStore = openStore(parsedURL.to_string()); auto fromStore = openStore(parsedURL.to_string());
if (toCA) { if (toPath)
if (!toPath || !state.store->isValidPath(*toPath)) { runFetchClosureWithRewrite(state, pos, *fromStore, *fromPath, *toPath, v);
auto remappings = makeContentAddressed(*fromStore, *state.store, { *fromPath }); else if (inputAddressed)
auto i = remappings.find(*fromPath); runFetchClosureWithInputAddressedPath(state, pos, *fromStore, *fromPath, v);
assert(i != remappings.end()); else
if (toPath && *toPath != i->second) runFetchClosureWithContentAddressedPath(state, pos, *fromStore, *fromPath, v);
throw Error({
.msg = hintfmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
state.store->printStorePath(*fromPath),
state.store->printStorePath(i->second),
state.store->printStorePath(*toPath)),
.errPos = state.positions[pos]
});
if (!toPath)
throw Error({
.msg = hintfmt(
"rewriting '%s' to content-addressed form yielded '%s'; "
"please set this in the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(*fromPath),
state.store->printStorePath(i->second)),
.errPos = state.positions[pos]
});
}
} else {
if (!state.store->isValidPath(*fromPath))
copyClosure(*fromStore, *state.store, RealisedPath::Set { *fromPath });
toPath = fromPath;
}
/* In pure mode, require a CA path. */
if (evalSettings.pureEval) {
auto info = state.store->queryPathInfo(*toPath);
if (!info->isContentAddressed(*state.store))
throw Error({
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
state.store->printStorePath(*toPath)),
.errPos = state.positions[pos]
});
}
state.mkStorePathString(*toPath, v);
} }
static RegisterPrimOp primop_fetchClosure({ static RegisterPrimOp primop_fetchClosure({
.name = "__fetchClosure", .name = "__fetchClosure",
.args = {"args"}, .args = {"args"},
.doc = R"( .doc = R"(
Fetch a Nix store closure from a binary cache, rewriting it into Fetch a store path [closure](@docroot@/glossary.md#gloss-closure) from a binary cache, and return the store path as a string with context.
content-addressed form. For example,
This function can be invoked in three ways, that we will discuss in order of preference.
**Fetch a content-addressed store path**
Example:
```nix
builtins.fetchClosure {
fromStore = "https://cache.nixos.org";
fromPath = /nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1;
}
```
This is the simplest invocation, and it does not require the user of the expression to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity.
If your store path is [input addressed](@docroot@/glossary.md#gloss-input-addressed-store-object) instead of content addressed, consider the other two invocations.
**Fetch any store path and rewrite it to a fully content-addressed store path**
Example:
```nix ```nix
builtins.fetchClosure { builtins.fetchClosure {
@ -132,31 +242,42 @@ static RegisterPrimOp primop_fetchClosure({
} }
``` ```
fetches `/nix/store/r2jd...` from the specified binary cache, This example fetches `/nix/store/r2jd...` from the specified binary cache,
and rewrites it into the content-addressed store path and rewrites it into the content-addressed store path
`/nix/store/ldbh...`. `/nix/store/ldbh...`.
If `fromPath` is already content-addressed, or if you are Like the previous example, no extra configuration or privileges are required.
allowing impure evaluation (`--impure`), then `toPath` may be
omitted.
To find out the correct value for `toPath` given a `fromPath`, To find out the correct value for `toPath` given a `fromPath`,
you can use `nix store make-content-addressed`: use [`nix store make-content-addressed`](@docroot@/command-ref/new-cli/nix3-store-make-content-addressed.md):
```console ```console
# nix store make-content-addressed --from https://cache.nixos.org /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1 # nix store make-content-addressed --from https://cache.nixos.org /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1
rewrote '/nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1' to '/nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1' rewrote '/nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1' to '/nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1'
``` ```
This function is similar to `builtins.storePath` in that it Alternatively, set `toPath = ""` and find the correct `toPath` in the error message.
allows you to use a previously built store path in a Nix
expression. However, it is more reproducible because it requires
specifying a binary cache from which the path can be fetched.
Also, requiring a content-addressed final store path avoids the
need for users to configure binary cache public keys.
This function is only available if you enable the experimental **Fetch an input-addressed store path as is**
feature `fetch-closure`.
Example:
```nix
builtins.fetchClosure {
fromStore = "https://cache.nixos.org";
fromPath = /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1;
inputAddressed = true;
}
```
It is possible to fetch an [input-addressed store path](@docroot@/glossary.md#gloss-input-addressed-store-object) and return it as is.
However, this is the least preferred way of invoking `fetchClosure`, because it requires that the input-addressed paths are trusted by the Nix configuration.
**`builtins.storePath`**
`fetchClosure` is similar to [`builtins.storePath`](#builtins-storePath) in that it allows you to use a previously built store path in a Nix expression.
However, `fetchClosure` is more reproducible because it specifies a binary cache from which the path can be fetched.
Also, using content-addressed store paths does not require users to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity.
)", )",
.fun = prim_fetchClosure, .fun = prim_fetchClosure,
.experimentalFeature = Xp::FetchClosure, .experimentalFeature = Xp::FetchClosure,

View file

@ -88,6 +88,10 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
state.allowPath(tree.storePath); state.allowPath(tree.storePath);
} }
static RegisterPrimOp r_fetchMercurial("fetchMercurial", 1, prim_fetchMercurial); static RegisterPrimOp r_fetchMercurial({
.name = "fetchMercurial",
.arity = 1,
.fun = prim_fetchMercurial
});
} }

View file

@ -22,7 +22,7 @@ void emitTreeAttrs(
{ {
assert(input.isLocked()); assert(input.isLocked());
auto attrs = state.buildBindings(8); auto attrs = state.buildBindings(10);
state.mkStorePathString(tree.storePath, attrs.alloc(state.sOutPath)); state.mkStorePathString(tree.storePath, attrs.alloc(state.sOutPath));
@ -56,6 +56,11 @@ void emitTreeAttrs(
} }
if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) {
attrs.alloc("dirtyRev").mkString(*dirtyRev);
attrs.alloc("dirtyShortRev").mkString(*fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev"));
}
if (auto lastModified = input.getLastModified()) { if (auto lastModified = input.getLastModified()) {
attrs.alloc("lastModified").mkInt(*lastModified); attrs.alloc("lastModified").mkInt(*lastModified);
attrs.alloc("lastModifiedDate").mkString( attrs.alloc("lastModifiedDate").mkString(
@ -194,7 +199,11 @@ static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args,
} }
// FIXME: document // FIXME: document
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree); static RegisterPrimOp primop_fetchTree({
.name = "fetchTree",
.arity = 1,
.fun = prim_fetchTree
});
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v, static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
const std::string & who, bool unpack, std::string name) const std::string & who, bool unpack, std::string name)
@ -262,7 +271,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
// https://github.com/NixOS/nix/issues/4313 // https://github.com/NixOS/nix/issues/4313
auto storePath = auto storePath =
unpack unpack
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath ? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).tree.storePath
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath; : fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
if (expectedHash) { if (expectedHash) {
@ -286,9 +295,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,
}); });
@ -338,8 +347,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,
}); });
@ -470,14 +478,9 @@ static RegisterPrimOp primop_fetchGit({
} }
``` ```
> **Note** Nix will refetch the branch according to the [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl) setting.
>
> Nix will refetch the branch in accordance with
> the option `tarball-ttl`.
> **Note** This behavior is disabled in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
>
> This behavior is disabled in *Pure evaluation mode*.
- To fetch the content of a checked-out work directory: - To fetch the content of a checked-out work directory:

View file

@ -3,6 +3,8 @@
#include "../../toml11/toml.hpp" #include "../../toml11/toml.hpp"
#include <sstream>
namespace nix { namespace nix {
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val) static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
@ -58,8 +60,18 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
case toml::value_t::offset_datetime: case toml::value_t::offset_datetime:
case toml::value_t::local_date: case toml::value_t::local_date:
case toml::value_t::local_time: case toml::value_t::local_time:
// We fail since Nix doesn't have date and time types {
throw std::runtime_error("Dates and times are not supported"); if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
auto attrs = state.buildBindings(2);
attrs.alloc("_type").mkString("timestamp");
std::ostringstream s;
s << t;
attrs.alloc("value").mkString(s.str());
v.mkAttrs(attrs);
} else {
throw std::runtime_error("Dates and times are not supported");
}
}
break;; break;;
case toml::value_t::empty: case toml::value_t::empty:
v.mkNull(); v.mkNull();
@ -78,6 +90,24 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
} }
} }
static RegisterPrimOp primop_fromTOML("fromTOML", 1, prim_fromTOML); static RegisterPrimOp primop_fromTOML({
.name = "fromTOML",
.args = {"e"},
.doc = R"(
Convert a TOML string to a Nix value. For example,
```nix
builtins.fromTOML ''
x=1
s="a"
[table]
y=2
''
```
returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
)",
.fun = prim_fromTOML
});
} }

View file

@ -0,0 +1,56 @@
#include "search-path.hh"
#include "util.hh"
namespace nix {
std::optional<std::string_view> SearchPath::Prefix::suffixIfPotentialMatch(
std::string_view path) const
{
auto n = s.size();
/* Non-empty prefix and suffix must be separated by a /, or the
prefix is not a valid path prefix. */
bool needSeparator = n > 0 && (path.size() - n) > 0;
if (needSeparator && path[n] != '/') {
return std::nullopt;
}
/* Prefix must be prefix of this path. */
if (path.compare(0, n, s) != 0) {
return std::nullopt;
}
/* Skip next path separator. */
return {
path.substr(needSeparator ? n + 1 : n)
};
}
SearchPath::Elem SearchPath::Elem::parse(std::string_view rawElem)
{
size_t pos = rawElem.find('=');
return SearchPath::Elem {
.prefix = Prefix {
.s = pos == std::string::npos
? std::string { "" }
: std::string { rawElem.substr(0, pos) },
},
.path = Path {
.s = std::string { rawElem.substr(pos + 1) },
},
};
}
SearchPath parseSearchPath(const Strings & rawElems)
{
SearchPath res;
for (auto & rawElem : rawElems)
res.elements.emplace_back(SearchPath::Elem::parse(rawElem));
return res;
}
}

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