Merge branch 'master' into indexed-store-path-outputs

This commit is contained in:
John Ericson 2022-10-28 23:22:18 +01:00 committed by GitHub
commit 13f2a6f38d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
153 changed files with 2341 additions and 1432 deletions

View file

@ -2,7 +2,7 @@
name: Feature request name: Feature request
about: Suggest an idea for this project about: Suggest an idea for this project
title: '' title: ''
labels: improvement labels: feature
assignees: '' assignees: ''
--- ---

View file

@ -0,0 +1,28 @@
---
name: Missing or incorrect documentation
about: Help us improve the reference manual
title: ''
labels: documentation
assignees: ''
---
## Problem
<!-- describe your problem -->
## Checklist
<!-- make sure this issue is not redundant or obsolete -->
- [ ] checked [latest Nix manual] \([source])
- [ ] checked [open documentation issues and pull requests] for possible duplicates
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/src
[open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation
## Proposal
<!-- propose a solution -->

View file

@ -2,9 +2,15 @@ name: Backport
on: on:
pull_request_target: pull_request_target:
types: [closed, labeled] types: [closed, labeled]
permissions:
contents: read
jobs: jobs:
backport: backport:
name: Backport Pull Request name: Backport Pull Request
permissions:
# for zeebe-io/backport-action
contents: write
pull-requests: write
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name)) if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View file

@ -9,7 +9,7 @@ permissions: read-all
jobs: jobs:
tests: tests:
needs: [check_cachix] needs: [check_secrets]
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest] os: [ubuntu-latest, macos-latest]
@ -19,33 +19,37 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: cachix/install-nix-action@v17 - uses: cachix/install-nix-action@v18
- 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@v10 - uses: cachix/cachix-action@v11
if: needs.check_cachix.outputs.secret == 'true' if: needs.check_secrets.outputs.cachix == 'true'
with: with:
name: '${{ env.CACHIX_NAME }}' name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix --experimental-features 'nix-command flakes' flake check -L - run: nix --experimental-features 'nix-command flakes' flake check -L
check_cachix: check_secrets:
permissions: permissions:
contents: none contents: none
name: Cachix secret present for installer tests name: Check Cachix and Docker secrets present for installer tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
secret: ${{ steps.secret.outputs.secret }} cachix: ${{ steps.secret.outputs.cachix }}
docker: ${{ steps.secret.outputs.docker }}
steps: steps:
- name: Check for Cachix secret - name: Check for secrets
id: secret id: secret
env: env:
_CACHIX_SECRETS: ${{ secrets.CACHIX_SIGNING_KEY }}${{ secrets.CACHIX_AUTH_TOKEN }} _CACHIX_SECRETS: ${{ secrets.CACHIX_SIGNING_KEY }}${{ secrets.CACHIX_AUTH_TOKEN }}
run: echo "::set-output name=secret::${{ env._CACHIX_SECRETS != '' }}" _DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }}
run: |
echo "::set-output name=cachix::${{ env._CACHIX_SECRETS != '' }}"
echo "::set-output name=docker::${{ env._DOCKER_SECRETS != '' }}"
installer: installer:
needs: [tests, check_cachix] needs: [tests, check_secrets]
if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true' if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
installerURL: ${{ steps.prepare-installer.outputs.installerURL }} installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
@ -54,8 +58,8 @@ 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@v17 - uses: cachix/install-nix-action@v18
- uses: cachix/cachix-action@v10 - uses: cachix/cachix-action@v11
with: with:
name: '${{ env.CACHIX_NAME }}' name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
@ -64,8 +68,8 @@ jobs:
run: scripts/prepare-installer-for-github-actions run: scripts/prepare-installer-for-github-actions
installer_test: installer_test:
needs: [installer, check_cachix] needs: [installer, check_secrets]
if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true' if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest] os: [ubuntu-latest, macos-latest]
@ -73,28 +77,36 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v17 - uses: cachix/install-nix-action@v18
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"
- run: nix-instantiate -E 'builtins.currentTime' --eval - run: sudo apt install fish zsh
if: matrix.os == 'ubuntu-latest'
- run: brew install fish
if: matrix.os == 'macos-latest'
- run: exec bash -c "nix-instantiate -E 'builtins.currentTime' --eval"
- run: exec sh -c "nix-instantiate -E 'builtins.currentTime' --eval"
- run: exec zsh -c "nix-instantiate -E 'builtins.currentTime' --eval"
- run: exec fish -c "nix-instantiate -E 'builtins.currentTime' --eval"
docker_push_image: docker_push_image:
needs: [check_cachix, tests] needs: [check_secrets, tests]
if: >- if: >-
github.event_name == 'push' && github.event_name == 'push' &&
github.ref_name == 'master' && github.ref_name == 'master' &&
needs.check_cachix.outputs.secret == 'true' needs.check_secrets.outputs.cachix == 'true' &&
needs.check_secrets.outputs.docker == 'true'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: cachix/install-nix-action@v17 - uses: cachix/install-nix-action@v18
- 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
- run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10 - uses: cachix/cachix-action@v11
if: needs.check_cachix.outputs.secret == 'true' if: needs.check_secrets.outputs.cachix == 'true'
with: with:
name: '${{ env.CACHIX_NAME }}' name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'

4
.gitignore vendored
View file

@ -22,11 +22,13 @@ perl/Makefile.config
/doc/manual/src/SUMMARY.md /doc/manual/src/SUMMARY.md
/doc/manual/src/command-ref/new-cli /doc/manual/src/command-ref/new-cli
/doc/manual/src/command-ref/conf-file.md /doc/manual/src/command-ref/conf-file.md
/doc/manual/src/expressions/builtins.md /doc/manual/src/language/builtins.md
# /scripts/ # /scripts/
/scripts/nix-profile.sh /scripts/nix-profile.sh
/scripts/nix-profile-daemon.sh /scripts/nix-profile-daemon.sh
/scripts/nix-profile.fish
/scripts/nix-profile-daemon.fish
# /src/libexpr/ # /src/libexpr/
/src/libexpr/lexer-tab.cc /src/libexpr/lexer-tab.cc

View file

@ -1 +1 @@
2.10.0 2.12.0

View file

@ -20,7 +20,7 @@ Information on additional installation methods is available on the [Nix download
## Building And Developing ## Building And Developing
See our [Hacking guide](https://hydra.nixos.org/job/nix/master/build.x86_64-linux/latest/download-by-type/doc/manual/contributing/hacking.html) in our manual for instruction on how to See our [Hacking guide](https://nixos.org/manual/nix/stable/contributing/hacking.html) in our manual for instruction on how to
build nix from source with nix-build or how to get a development environment. build nix from source with nix-build or how to get a development environment.
## Additional Resources ## Additional Resources

View file

@ -1,3 +1,35 @@
diff --git a/darwin_stop_world.c b/darwin_stop_world.c
index 3dbaa3fb..36a1d1f7 100644
--- a/darwin_stop_world.c
+++ b/darwin_stop_world.c
@@ -352,6 +352,7 @@ GC_INNER void GC_push_all_stacks(void)
int nthreads = 0;
word total_size = 0;
mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ;
+ size_t stack_limit;
if (!EXPECT(GC_thr_initialized, TRUE))
GC_thr_init();
@@ -407,6 +408,19 @@ GC_INNER void GC_push_all_stacks(void)
GC_push_all_stack_sections(lo, hi, p->traced_stack_sect);
}
if (altstack_lo) {
+ // When a thread goes into a coroutine, we lose its original sp until
+ // control flow returns to the thread.
+ // While in the coroutine, the sp points outside the thread stack,
+ // so we can detect this and push the entire thread stack instead,
+ // as an approximation.
+ // We assume that the coroutine has similarly added its entire stack.
+ // This could be made accurate by cooperating with the application
+ // via new functions and/or callbacks.
+ stack_limit = pthread_get_stacksize_np(p->id);
+ if (altstack_lo >= altstack_hi || altstack_lo < altstack_hi - stack_limit) { // sp outside stack
+ altstack_lo = altstack_hi - stack_limit;
+ }
+
total_size += altstack_hi - altstack_lo;
GC_push_all_stack(altstack_lo, altstack_hi);
}
diff --git a/pthread_stop_world.c b/pthread_stop_world.c diff --git a/pthread_stop_world.c b/pthread_stop_world.c
index 4b2c429..1fb4c52 100644 index 4b2c429..1fb4c52 100644
--- a/pthread_stop_world.c --- a/pthread_stop_world.c

View file

@ -296,15 +296,6 @@ AC_CHECK_FUNCS([setresuid setreuid lchown])
AC_CHECK_FUNCS([strsignal posix_fallocate sysconf]) AC_CHECK_FUNCS([strsignal posix_fallocate sysconf])
# This is needed if bzip2 is a static library, and the Nix libraries
# are dynamic.
case "${host_os}" in
darwin*)
LDFLAGS="-all_load $LDFLAGS"
;;
esac
AC_ARG_WITH(sandbox-shell, AS_HELP_STRING([--with-sandbox-shell=PATH],[path of a statically-linked shell to use as /bin/sh in sandboxes]), AC_ARG_WITH(sandbox-shell, AS_HELP_STRING([--with-sandbox-shell=PATH],[path of a statically-linked shell to use as /bin/sh in sandboxes]),
sandbox_shell=$withval) sandbox_shell=$withval)
AC_SUBST(sandbox_shell) AC_SUBST(sandbox_shell)

View file

@ -1,99 +1,114 @@
{ command, renderLinks ? false }: { toplevel }:
with builtins; with builtins;
with import ./utils.nix; with import ./utils.nix;
let let
showCommand = showCommand = { command, details, filename, toplevel }:
{ command, def, filename }: let
'' result = ''
**Warning**: This program is **experimental** and its interface is subject to change. > **Warning** \
'' > This program is **experimental** and its interface is subject to change.
+ "# Name\n\n"
+ "`${command}` - ${def.description}\n\n" # Name
+ "# Synopsis\n\n"
+ showSynopsis { inherit command; args = def.args; } `${command}` - ${details.description}
+ (if def.commands or {} != {}
then # Synopsis
let
categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues def.commands))); ${showSynopsis command details.args}
listCommands = cmds:
concatStrings (map (name: ${maybeSubcommands}
"* "
+ (if renderLinks ${maybeDocumentation}
then "[`${command} ${name}`](./${appendName filename name}.md)"
else "`${command} ${name}`") ${maybeOptions}
+ " - ${cmds.${name}.description}\n") '';
(attrNames cmds)); showSynopsis = command: args:
in let
"where *subcommand* is one of the following:\n\n" showArgument = arg: "*${arg.label}*" + (if arg ? arity then "" else "...");
# FIXME: group by category arguments = concatStringsSep " " (map showArgument args);
+ (if length categories > 1 in ''
then `${command}` [*option*...] ${arguments}
concatStrings (map '';
(cat: maybeSubcommands = if details ? commands && details.commands != {}
"**${toString cat.description}:**\n\n" then ''
+ listCommands (filterAttrs (n: v: v.category == cat) def.commands) where *subcommand* is one of the following:
+ "\n"
) categories) ${subcommands}
+ "\n" ''
else else "";
listCommands def.commands subcommands = if length categories > 1
+ "\n") then listCategories
else "") else listSubcommands details.commands;
+ (if def ? doc categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues details.commands)));
then def.doc + "\n\n" listCategories = concatStrings (map showCategory categories);
else "") showCategory = cat: ''
+ (let s = showOptions def.flags; in **${toString cat.description}:**
if s != ""
then "# Options\n\n${s}" ${listSubcommands (filterAttrs (n: v: v.category == cat) details.commands)}
else "") '';
; listSubcommands = cmds: concatStrings (attrValues (mapAttrs showSubcommand cmds));
showSubcommand = name: subcmd: ''
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
'';
maybeDocumentation = if details ? doc then details.doc else "";
maybeOptions = if details.flags == {} then "" else ''
# Options
${showOptions details.flags toplevel.flags}
'';
showOptions = options: commonOptions:
let
allOptions = options // commonOptions;
showCategory = cat: ''
${if cat != "" then "**${cat}:**" else ""}
${listOptions (filterAttrs (n: v: v.category == cat) allOptions)}
'';
listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts));
showOption = name: option:
let
shortName = if option ? shortName then "/ `-${option.shortName}`" else "";
labels = if option ? labels then (concatStringsSep " " (map (s: "*${s}*") option.labels)) else "";
in trim ''
- `--${name}` ${shortName} ${labels}
${option.description}
'';
categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues allOptions)));
in concatStrings (map showCategory categories);
in squash result;
appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name; appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name;
showOptions = flags: processCommand = { command, details, filename, toplevel }:
let let
categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues flags))); cmd = {
in inherit command;
concatStrings (map name = filename + ".md";
(cat: value = showCommand { inherit command details filename toplevel; };
(if cat != "" };
then "**${cat}:**\n\n" subcommand = subCmd: processCommand {
else "") command = command + " " + subCmd;
+ concatStrings details = details.commands.${subCmd};
(map (longName: filename = appendName filename subCmd;
let inherit toplevel;
flag = flags.${longName}; };
in in [ cmd ] ++ concatMap subcommand (attrNames details.commands or {});
" - `--${longName}`"
+ (if flag ? shortName then " / `-${flag.shortName}`" else "")
+ (if flag ? labels then " " + (concatStringsSep " " (map (s: "*${s}*") flag.labels)) else "")
+ " \n"
+ " " + flag.description + "\n\n"
) (attrNames (filterAttrs (n: v: v.category == cat) flags))))
categories);
showSynopsis = parsedToplevel = builtins.fromJSON toplevel;
{ command, args }: manpages = processCommand {
"`${command}` [*option*...] ${concatStringsSep " " command = "nix";
(map (arg: "*${arg.label}*" + (if arg ? arity then "" else "...")) args)}\n\n"; details = parsedToplevel;
filename = "nix";
toplevel = parsedToplevel;
};
processCommand = { command, def, filename }: tableOfContents = let
[ { name = filename + ".md"; value = showCommand { inherit command def filename; }; inherit command; } ] showEntry = page:
++ concatMap " - [${page.command}](command-ref/new-cli/${page.name})";
(name: processCommand { in concatStringsSep "\n" (map showEntry manpages) + "\n";
filename = appendName filename name;
command = command + " " + name;
def = def.commands.${name};
})
(attrNames def.commands or {});
in in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; }
let
manpages = processCommand { filename = "nix"; command = "nix"; def = builtins.fromJSON command; };
summary = concatStrings (map (manpage: " - [${manpage.command}](command-ref/new-cli/${manpage.name})\n") manpages);
in
(listToAttrs manpages) // { "SUMMARY.md" = summary; }

View file

@ -11,16 +11,16 @@ concatStrings (map
+ concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n" + concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n"
+ (if option.documentDefault + (if option.documentDefault
then " **Default:** " + ( then " **Default:** " + (
if option.value == "" || option.value == [] if option.defaultValue == "" || option.defaultValue == []
then "*empty*" then "*empty*"
else if isBool option.value else if isBool option.defaultValue
then (if option.value then "`true`" else "`false`") then (if option.defaultValue then "`true`" else "`false`")
else else
# n.b. a StringMap value type is specified as a string, but # n.b. a StringMap value type is specified as a string, but
# this shows the value type. The empty stringmap is "null" in # this shows the value type. The empty stringmap is "null" in
# JSON, but that converts to "{ }" here. # JSON, but that converts to "{ }" here.
(if isAttrs option.value then "`\"\"`" (if isAttrs option.defaultValue then "`\"\"`"
else "`" + toString option.value + "`")) + "\n\n" else "`" + toString option.defaultValue + "`")) + "\n\n"
else " **Default:** *machine-specific*\n") else " **Default:** *machine-specific*\n")
+ (if option.aliases != [] + (if option.aliases != []
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n" then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"

View file

@ -1,5 +1,9 @@
ifeq ($(doc_generate),yes) ifeq ($(doc_generate),yes)
MANUAL_SRCS := \
$(call rwildcard, $(d)/src, *.md) \
$(call rwildcard, $(d)/src, */*.md)
# Generate man pages. # Generate man pages.
man-pages := $(foreach n, \ man-pages := $(foreach n, \
nix-env.1 nix-build.1 nix-shell.1 nix-store.1 nix-instantiate.1 \ nix-env.1 nix-build.1 nix-shell.1 nix-store.1 nix-instantiate.1 \
@ -46,7 +50,7 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix $(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
@rm -rf $@ @rm -rf $@
$(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix { command = builtins.readFile $<; renderLinks = true; }' $(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix { toplevel = builtins.readFile $<; }'
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix $(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp @cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
@ -61,10 +65,10 @@ $(d)/conf-file.json: $(bindir)/nix
$(trace-gen) $(dummy-env) $(bindir)/nix show-config --json --experimental-features nix-command > $@.tmp $(trace-gen) $(dummy-env) $(bindir)/nix show-config --json --experimental-features nix-command > $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/src/expressions/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/expressions/builtins-prefix.md $(bindir)/nix $(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
@cat doc/manual/src/expressions/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 $<))' >> $@.tmp
@cat doc/manual/src/expressions/builtins-suffix.md >> $@.tmp @cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/builtins.json: $(bindir)/nix $(d)/builtins.json: $(bindir)/nix
@ -92,12 +96,12 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
if [[ $$name = SUMMARY ]]; then continue; fi; \ if [[ $$name = SUMMARY ]]; then continue; fi; \
printf "Title: %s\n\n" "$$name" > $$tmpFile; \ printf "Title: %s\n\n" "$$name" > $$tmpFile; \
cat $$i >> $$tmpFile; \ cat $$i >> $$tmpFile; \
lowdown -sT man -M section=1 $$tmpFile -o $(DESTDIR)$$(dirname $@)/$$name.1; \ lowdown -sT man --nroff-nolinks -M section=1 $$tmpFile -o $(DESTDIR)$$(dirname $@)/$$name.1; \
rm $$tmpFile; \ rm $$tmpFile; \
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/command-ref/conf-file.md $(d)/src/expressions/builtins.md $(call rwildcard, $(d)/src, *.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/command-ref/conf-file.md $(d)/src/language/builtins.md
$(trace-gen) RUST_LOG=warn mdbook build doc/manual -d $(DESTDIR)$(docdir)/manual $(trace-gen) RUST_LOG=warn mdbook build doc/manual -d $(DESTDIR)$(docdir)/manual
endif endif

View file

@ -132,113 +132,106 @@ var redirects = {
"#sec-common-options": "command-ref/opt-common.html", "#sec-common-options": "command-ref/opt-common.html",
"#ch-utilities": "command-ref/utilities.html", "#ch-utilities": "command-ref/utilities.html",
"#chap-hacking": "contributing/hacking.html", "#chap-hacking": "contributing/hacking.html",
"#adv-attr-allowSubstitutes": "expressions/advanced-attributes.html#adv-attr-allowSubstitutes", "#adv-attr-allowSubstitutes": "language/advanced-attributes.html#adv-attr-allowSubstitutes",
"#adv-attr-allowedReferences": "expressions/advanced-attributes.html#adv-attr-allowedReferences", "#adv-attr-allowedReferences": "language/advanced-attributes.html#adv-attr-allowedReferences",
"#adv-attr-allowedRequisites": "expressions/advanced-attributes.html#adv-attr-allowedRequisites", "#adv-attr-allowedRequisites": "language/advanced-attributes.html#adv-attr-allowedRequisites",
"#adv-attr-disallowedReferences": "expressions/advanced-attributes.html#adv-attr-disallowedReferences", "#adv-attr-disallowedReferences": "language/advanced-attributes.html#adv-attr-disallowedReferences",
"#adv-attr-disallowedRequisites": "expressions/advanced-attributes.html#adv-attr-disallowedRequisites", "#adv-attr-disallowedRequisites": "language/advanced-attributes.html#adv-attr-disallowedRequisites",
"#adv-attr-exportReferencesGraph": "expressions/advanced-attributes.html#adv-attr-exportReferencesGraph", "#adv-attr-exportReferencesGraph": "language/advanced-attributes.html#adv-attr-exportReferencesGraph",
"#adv-attr-impureEnvVars": "expressions/advanced-attributes.html#adv-attr-impureEnvVars", "#adv-attr-impureEnvVars": "language/advanced-attributes.html#adv-attr-impureEnvVars",
"#adv-attr-outputHash": "expressions/advanced-attributes.html#adv-attr-outputHash", "#adv-attr-outputHash": "language/advanced-attributes.html#adv-attr-outputHash",
"#adv-attr-outputHashAlgo": "expressions/advanced-attributes.html#adv-attr-outputHashAlgo", "#adv-attr-outputHashAlgo": "language/advanced-attributes.html#adv-attr-outputHashAlgo",
"#adv-attr-outputHashMode": "expressions/advanced-attributes.html#adv-attr-outputHashMode", "#adv-attr-outputHashMode": "language/advanced-attributes.html#adv-attr-outputHashMode",
"#adv-attr-passAsFile": "expressions/advanced-attributes.html#adv-attr-passAsFile", "#adv-attr-passAsFile": "language/advanced-attributes.html#adv-attr-passAsFile",
"#adv-attr-preferLocalBuild": "expressions/advanced-attributes.html#adv-attr-preferLocalBuild", "#adv-attr-preferLocalBuild": "language/advanced-attributes.html#adv-attr-preferLocalBuild",
"#fixed-output-drvs": "expressions/advanced-attributes.html#adv-attr-outputHash", "#fixed-output-drvs": "language/advanced-attributes.html#adv-attr-outputHash",
"#sec-advanced-attributes": "expressions/advanced-attributes.html", "#sec-advanced-attributes": "language/advanced-attributes.html",
"#sec-arguments": "expressions/arguments-variables.html", "#builtin-abort": "language/builtins.html#builtins-abort",
"#sec-build-script": "expressions/build-script.html", "#builtin-add": "language/builtins.html#builtins-add",
"#builtin-abort": "expressions/builtins.html#builtins-abort", "#builtin-all": "language/builtins.html#builtins-all",
"#builtin-add": "expressions/builtins.html#builtins-add", "#builtin-any": "language/builtins.html#builtins-any",
"#builtin-all": "expressions/builtins.html#builtins-all", "#builtin-attrNames": "language/builtins.html#builtins-attrNames",
"#builtin-any": "expressions/builtins.html#builtins-any", "#builtin-attrValues": "language/builtins.html#builtins-attrValues",
"#builtin-attrNames": "expressions/builtins.html#builtins-attrNames", "#builtin-baseNameOf": "language/builtins.html#builtins-baseNameOf",
"#builtin-attrValues": "expressions/builtins.html#builtins-attrValues", "#builtin-bitAnd": "language/builtins.html#builtins-bitAnd",
"#builtin-baseNameOf": "expressions/builtins.html#builtins-baseNameOf", "#builtin-bitOr": "language/builtins.html#builtins-bitOr",
"#builtin-bitAnd": "expressions/builtins.html#builtins-bitAnd", "#builtin-bitXor": "language/builtins.html#builtins-bitXor",
"#builtin-bitOr": "expressions/builtins.html#builtins-bitOr", "#builtin-builtins": "language/builtins.html#builtins-builtins",
"#builtin-bitXor": "expressions/builtins.html#builtins-bitXor", "#builtin-compareVersions": "language/builtins.html#builtins-compareVersions",
"#builtin-builtins": "expressions/builtins.html#builtins-builtins", "#builtin-concatLists": "language/builtins.html#builtins-concatLists",
"#builtin-compareVersions": "expressions/builtins.html#builtins-compareVersions", "#builtin-concatStringsSep": "language/builtins.html#builtins-concatStringsSep",
"#builtin-concatLists": "expressions/builtins.html#builtins-concatLists", "#builtin-currentSystem": "language/builtins.html#builtins-currentSystem",
"#builtin-concatStringsSep": "expressions/builtins.html#builtins-concatStringsSep", "#builtin-deepSeq": "language/builtins.html#builtins-deepSeq",
"#builtin-currentSystem": "expressions/builtins.html#builtins-currentSystem", "#builtin-derivation": "language/builtins.html#builtins-derivation",
"#builtin-deepSeq": "expressions/builtins.html#builtins-deepSeq", "#builtin-dirOf": "language/builtins.html#builtins-dirOf",
"#builtin-derivation": "expressions/builtins.html#builtins-derivation", "#builtin-div": "language/builtins.html#builtins-div",
"#builtin-dirOf": "expressions/builtins.html#builtins-dirOf", "#builtin-elem": "language/builtins.html#builtins-elem",
"#builtin-div": "expressions/builtins.html#builtins-div", "#builtin-elemAt": "language/builtins.html#builtins-elemAt",
"#builtin-elem": "expressions/builtins.html#builtins-elem", "#builtin-fetchGit": "language/builtins.html#builtins-fetchGit",
"#builtin-elemAt": "expressions/builtins.html#builtins-elemAt", "#builtin-fetchTarball": "language/builtins.html#builtins-fetchTarball",
"#builtin-fetchGit": "expressions/builtins.html#builtins-fetchGit", "#builtin-fetchurl": "language/builtins.html#builtins-fetchurl",
"#builtin-fetchTarball": "expressions/builtins.html#builtins-fetchTarball", "#builtin-filterSource": "language/builtins.html#builtins-filterSource",
"#builtin-fetchurl": "expressions/builtins.html#builtins-fetchurl", "#builtin-foldl-prime": "language/builtins.html#builtins-foldl-prime",
"#builtin-filterSource": "expressions/builtins.html#builtins-filterSource", "#builtin-fromJSON": "language/builtins.html#builtins-fromJSON",
"#builtin-foldl-prime": "expressions/builtins.html#builtins-foldl-prime", "#builtin-functionArgs": "language/builtins.html#builtins-functionArgs",
"#builtin-fromJSON": "expressions/builtins.html#builtins-fromJSON", "#builtin-genList": "language/builtins.html#builtins-genList",
"#builtin-functionArgs": "expressions/builtins.html#builtins-functionArgs", "#builtin-getAttr": "language/builtins.html#builtins-getAttr",
"#builtin-genList": "expressions/builtins.html#builtins-genList", "#builtin-getEnv": "language/builtins.html#builtins-getEnv",
"#builtin-getAttr": "expressions/builtins.html#builtins-getAttr", "#builtin-hasAttr": "language/builtins.html#builtins-hasAttr",
"#builtin-getEnv": "expressions/builtins.html#builtins-getEnv", "#builtin-hashFile": "language/builtins.html#builtins-hashFile",
"#builtin-hasAttr": "expressions/builtins.html#builtins-hasAttr", "#builtin-hashString": "language/builtins.html#builtins-hashString",
"#builtin-hashFile": "expressions/builtins.html#builtins-hashFile", "#builtin-head": "language/builtins.html#builtins-head",
"#builtin-hashString": "expressions/builtins.html#builtins-hashString", "#builtin-import": "language/builtins.html#builtins-import",
"#builtin-head": "expressions/builtins.html#builtins-head", "#builtin-intersectAttrs": "language/builtins.html#builtins-intersectAttrs",
"#builtin-import": "expressions/builtins.html#builtins-import", "#builtin-isAttrs": "language/builtins.html#builtins-isAttrs",
"#builtin-intersectAttrs": "expressions/builtins.html#builtins-intersectAttrs", "#builtin-isBool": "language/builtins.html#builtins-isBool",
"#builtin-isAttrs": "expressions/builtins.html#builtins-isAttrs", "#builtin-isFloat": "language/builtins.html#builtins-isFloat",
"#builtin-isBool": "expressions/builtins.html#builtins-isBool", "#builtin-isFunction": "language/builtins.html#builtins-isFunction",
"#builtin-isFloat": "expressions/builtins.html#builtins-isFloat", "#builtin-isInt": "language/builtins.html#builtins-isInt",
"#builtin-isFunction": "expressions/builtins.html#builtins-isFunction", "#builtin-isList": "language/builtins.html#builtins-isList",
"#builtin-isInt": "expressions/builtins.html#builtins-isInt", "#builtin-isNull": "language/builtins.html#builtins-isNull",
"#builtin-isList": "expressions/builtins.html#builtins-isList", "#builtin-isString": "language/builtins.html#builtins-isString",
"#builtin-isNull": "expressions/builtins.html#builtins-isNull", "#builtin-length": "language/builtins.html#builtins-length",
"#builtin-isString": "expressions/builtins.html#builtins-isString", "#builtin-lessThan": "language/builtins.html#builtins-lessThan",
"#builtin-length": "expressions/builtins.html#builtins-length", "#builtin-listToAttrs": "language/builtins.html#builtins-listToAttrs",
"#builtin-lessThan": "expressions/builtins.html#builtins-lessThan", "#builtin-map": "language/builtins.html#builtins-map",
"#builtin-listToAttrs": "expressions/builtins.html#builtins-listToAttrs", "#builtin-match": "language/builtins.html#builtins-match",
"#builtin-map": "expressions/builtins.html#builtins-map", "#builtin-mul": "language/builtins.html#builtins-mul",
"#builtin-match": "expressions/builtins.html#builtins-match", "#builtin-parseDrvName": "language/builtins.html#builtins-parseDrvName",
"#builtin-mul": "expressions/builtins.html#builtins-mul", "#builtin-path": "language/builtins.html#builtins-path",
"#builtin-parseDrvName": "expressions/builtins.html#builtins-parseDrvName", "#builtin-pathExists": "language/builtins.html#builtins-pathExists",
"#builtin-path": "expressions/builtins.html#builtins-path", "#builtin-placeholder": "language/builtins.html#builtins-placeholder",
"#builtin-pathExists": "expressions/builtins.html#builtins-pathExists", "#builtin-readDir": "language/builtins.html#builtins-readDir",
"#builtin-placeholder": "expressions/builtins.html#builtins-placeholder", "#builtin-readFile": "language/builtins.html#builtins-readFile",
"#builtin-readDir": "expressions/builtins.html#builtins-readDir", "#builtin-removeAttrs": "language/builtins.html#builtins-removeAttrs",
"#builtin-readFile": "expressions/builtins.html#builtins-readFile", "#builtin-replaceStrings": "language/builtins.html#builtins-replaceStrings",
"#builtin-removeAttrs": "expressions/builtins.html#builtins-removeAttrs", "#builtin-seq": "language/builtins.html#builtins-seq",
"#builtin-replaceStrings": "expressions/builtins.html#builtins-replaceStrings", "#builtin-sort": "language/builtins.html#builtins-sort",
"#builtin-seq": "expressions/builtins.html#builtins-seq", "#builtin-split": "language/builtins.html#builtins-split",
"#builtin-sort": "expressions/builtins.html#builtins-sort", "#builtin-splitVersion": "language/builtins.html#builtins-splitVersion",
"#builtin-split": "expressions/builtins.html#builtins-split", "#builtin-stringLength": "language/builtins.html#builtins-stringLength",
"#builtin-splitVersion": "expressions/builtins.html#builtins-splitVersion", "#builtin-sub": "language/builtins.html#builtins-sub",
"#builtin-stringLength": "expressions/builtins.html#builtins-stringLength", "#builtin-substring": "language/builtins.html#builtins-substring",
"#builtin-sub": "expressions/builtins.html#builtins-sub", "#builtin-tail": "language/builtins.html#builtins-tail",
"#builtin-substring": "expressions/builtins.html#builtins-substring", "#builtin-throw": "language/builtins.html#builtins-throw",
"#builtin-tail": "expressions/builtins.html#builtins-tail", "#builtin-toFile": "language/builtins.html#builtins-toFile",
"#builtin-throw": "expressions/builtins.html#builtins-throw", "#builtin-toJSON": "language/builtins.html#builtins-toJSON",
"#builtin-toFile": "expressions/builtins.html#builtins-toFile", "#builtin-toPath": "language/builtins.html#builtins-toPath",
"#builtin-toJSON": "expressions/builtins.html#builtins-toJSON", "#builtin-toString": "language/builtins.html#builtins-toString",
"#builtin-toPath": "expressions/builtins.html#builtins-toPath", "#builtin-toXML": "language/builtins.html#builtins-toXML",
"#builtin-toString": "expressions/builtins.html#builtins-toString", "#builtin-trace": "language/builtins.html#builtins-trace",
"#builtin-toXML": "expressions/builtins.html#builtins-toXML", "#builtin-tryEval": "language/builtins.html#builtins-tryEval",
"#builtin-trace": "expressions/builtins.html#builtins-trace", "#builtin-typeOf": "language/builtins.html#builtins-typeOf",
"#builtin-tryEval": "expressions/builtins.html#builtins-tryEval", "#ssec-builtins": "language/builtins.html",
"#builtin-typeOf": "expressions/builtins.html#builtins-typeOf", "#attr-system": "language/derivations.html#attr-system",
"#ssec-builtins": "expressions/builtins.html", "#ssec-derivation": "language/derivations.html",
"#attr-system": "expressions/derivations.html#attr-system", "#ch-expression-language": "language/index.html",
"#ssec-derivation": "expressions/derivations.html", "#sec-constructs": "language/constructs.html",
"#ch-expression-language": "expressions/expression-language.html", "#sect-let-language": "language/constructs.html#let-language",
"#sec-expression-syntax": "expressions/expression-syntax.html", "#ss-functions": "language/constructs.html#functions",
"#sec-generic-builder": "expressions/generic-builder.html", "#sec-language-operators": "language/operators.html",
"#sec-constructs": "expressions/language-constructs.html", "#table-operators": "language/operators.html",
"#sect-let-expressions": "expressions/language-constructs.html#let-expressions", "#ssec-values": "language/values.html",
"#ss-functions": "expressions/language-constructs.html#functions",
"#sec-language-operators": "expressions/language-operators.html",
"#table-operators": "expressions/language-operators.html",
"#ssec-values": "expressions/language-values.html",
"#sec-building-simple": "expressions/simple-building-testing.html",
"#ch-simple-expression": "expressions/simple-expression.html",
"#chap-writing-nix-expressions": "expressions/writing-nix-expressions.html",
"#gloss-closure": "glossary.html#gloss-closure", "#gloss-closure": "glossary.html#gloss-closure",
"#gloss-derivation": "glossary.html#gloss-derivation", "#gloss-derivation": "glossary.html#gloss-derivation",
"#gloss-deriver": "glossary.html#gloss-deriver", "#gloss-deriver": "glossary.html#gloss-deriver",

View file

@ -26,21 +26,14 @@
- [Copying Closures via SSH](package-management/copy-closure.md) - [Copying Closures via SSH](package-management/copy-closure.md)
- [Serving a Nix store via SSH](package-management/ssh-substituter.md) - [Serving a Nix store via SSH](package-management/ssh-substituter.md)
- [Serving a Nix store via S3](package-management/s3-substituter.md) - [Serving a Nix store via S3](package-management/s3-substituter.md)
- [Writing Nix Expressions](expressions/writing-nix-expressions.md) - [Nix Language](language/index.md)
- [A Simple Nix Expression](expressions/simple-expression.md) - [Data Types](language/values.md)
- [Expression Syntax](expressions/expression-syntax.md) - [Language Constructs](language/constructs.md)
- [Build Script](expressions/build-script.md) - [Operators](language/operators.md)
- [Arguments and Variables](expressions/arguments-variables.md) - [Derivations](language/derivations.md)
- [Building and Testing](expressions/simple-building-testing.md) - [Advanced Attributes](language/advanced-attributes.md)
- [Generic Builder Syntax](expressions/generic-builder.md) - [Built-in Constants](language/builtin-constants.md)
- [Writing Nix Expressions](expressions/expression-language.md) - [Built-in Functions](language/builtins.md)
- [Values](expressions/language-values.md)
- [Language Constructs](expressions/language-constructs.md)
- [Operators](expressions/language-operators.md)
- [Derivations](expressions/derivations.md)
- [Advanced Attributes](expressions/advanced-attributes.md)
- [Built-in Constants](expressions/builtin-constants.md)
- [Built-in Functions](expressions/builtins.md)
- [Advanced Topics](advanced-topics/advanced-topics.md) - [Advanced Topics](advanced-topics/advanced-topics.md)
- [Remote Builds](advanced-topics/distributed-builds.md) - [Remote Builds](advanced-topics/distributed-builds.md)
- [Tuning Cores and Jobs](advanced-topics/cores-vs-jobs.md) - [Tuning Cores and Jobs](advanced-topics/cores-vs-jobs.md)
@ -72,6 +65,7 @@
- [CLI guideline](contributing/cli-guideline.md) - [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md) - [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md) - [Release X.Y (202?-??-??)](release-notes/rl-next.md)
- [Release 2.11 (2022-08-25)](release-notes/rl-2.11.md)
- [Release 2.10 (2022-07-11)](release-notes/rl-2.10.md) - [Release 2.10 (2022-07-11)](release-notes/rl-2.10.md)
- [Release 2.9 (2022-05-30)](release-notes/rl-2.9.md) - [Release 2.9 (2022-05-30)](release-notes/rl-2.9.md)
- [Release 2.8 (2022-04-19)](release-notes/rl-2.8.md) - [Release 2.8 (2022-04-19)](release-notes/rl-2.8.md)

View file

@ -12,14 +12,14 @@ machine is accessible via SSH and that it has Nix installed. You can
test whether connecting to the remote Nix instance works, e.g. test whether connecting to the remote Nix instance works, e.g.
```console ```console
$ nix ping-store --store ssh://mac $ nix store ping --store ssh://mac
``` ```
will try to connect to the machine named `mac`. It is possible to will try to connect to the machine named `mac`. It is possible to
specify an SSH identity file as part of the remote store URI, e.g. specify an SSH identity file as part of the remote store URI, e.g.
```console ```console
$ nix ping-store --store ssh://mac?ssh-key=/home/alice/my-key $ nix store ping --store ssh://mac?ssh-key=/home/alice/my-key
``` ```
Since builds should be non-interactive, the key should not have a Since builds should be non-interactive, the key should not have a

View file

@ -30,8 +30,8 @@ Since `nix-copy-closure` calls `ssh`, you may be asked to type in the
appropriate password or passphrase. In fact, you may be asked _twice_ appropriate password or passphrase. In fact, you may be asked _twice_
because `nix-copy-closure` currently connects twice to the remote because `nix-copy-closure` currently connects twice to the remote
machine, first to get the set of paths missing on the target machine, machine, first to get the set of paths missing on the target machine,
and second to send the dump of those paths. If this bothers you, use and second to send the dump of those paths. When using public key
`ssh-agent`. authentication, you can avoid typing the passphrase with `ssh-agent`.
# Options # Options

View file

@ -198,7 +198,7 @@ a number of possible ways:
another. another.
- If `--from-expression` is given, *args* are Nix - If `--from-expression` is given, *args* are Nix
[functions](../expressions/language-constructs.md#functions) [functions](../language/constructs.md#functions)
that are called with the active Nix expression as their single that are called with the active Nix expression as their single
argument. The derivations returned by those function calls are argument. The derivations returned by those function calls are
installed. This allows derivations to be specified in an installed. This allows derivations to be specified in an

View file

@ -51,7 +51,7 @@ standard input.
- `--strict`\ - `--strict`\
When used with `--eval`, recursively evaluate list elements and When used with `--eval`, recursively evaluate list elements and
attributes. Normally, such sub-expressions are left unevaluated attributes. Normally, such sub-expressions are left unevaluated
(since the Nix expression language is lazy). (since the Nix language is lazy).
> **Warning** > **Warning**
> >
@ -66,7 +66,7 @@ standard input.
When used with `--eval`, print the resulting value as an XML When used with `--eval`, print the resulting value as an XML
representation of the abstract syntax tree rather than as an ATerm. representation of the abstract syntax tree rather than as an ATerm.
The schema is the same as that used by the [`toXML` The schema is the same as that used by the [`toXML`
built-in](../expressions/builtins.md). built-in](../language/builtins.md).
- `--read-write-mode`\ - `--read-write-mode`\
When used with `--eval`, perform evaluation in read/write mode so When used with `--eval`, perform evaluation in read/write mode so

View file

@ -121,7 +121,7 @@ Special exit codes:
- `102`\ - `102`\
Hash mismatch, the build output was rejected because it does not Hash mismatch, the build output was rejected because it does not
match the [`outputHash` attribute of the match the [`outputHash` attribute of the
derivation](../expressions/advanced-attributes.md). derivation](../language/advanced-attributes.md).
- `104`\ - `104`\
Not deterministic, the build succeeded in check mode but the Not deterministic, the build succeeded in check mode but the

View file

@ -145,7 +145,7 @@ Most Nix commands accept the following command-line options:
expression evaluator will automatically try to call functions that expression evaluator will automatically try to call functions that
it encounters. It can automatically call functions for which every it encounters. It can automatically call functions for which every
argument has a [default argument has a [default
value](../expressions/language-constructs.md#functions) (e.g., value](../language/constructs.md#functions) (e.g.,
`{ argName ? defaultValue }: ...`). With `--arg`, you can also `{ argName ? defaultValue }: ...`). With `--arg`, you can also
call functions that have arguments without a default value (or call functions that have arguments without a default value (or
override a default value). That is, if the evaluator encounters a override a default value). That is, if the evaluator encounters a
@ -164,7 +164,7 @@ 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 -iA
pkgname`), the function will be called automatically using the pkgname`), the function will be called automatically using the
value [`builtins.currentSystem`](../expressions/builtins.md) for value [`builtins.currentSystem`](../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 -iA 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

View file

@ -42,7 +42,7 @@ $ nix develop
``` ```
To get a shell with a different compilation environment (e.g. stdenv, To get a shell with a different compilation environment (e.g. stdenv,
gccStdenv, clangStdenv, clang11Stdenv): gccStdenv, clangStdenv, clang11Stdenv, ccacheStdenv):
```console ```console
$ nix-shell -A devShells.x86_64-linux.clang11StdenvPackages $ nix-shell -A devShells.x86_64-linux.clang11StdenvPackages
@ -54,6 +54,9 @@ or if you have a flake-enabled nix:
$ nix develop .#clang11StdenvPackages $ nix develop .#clang11StdenvPackages
``` ```
Note: you can use `ccacheStdenv` to drastically improve rebuild
time. By default, ccache keeps artifacts in `~/.cache/ccache/`.
To build Nix itself in this shell: To build Nix itself in this shell:
```console ```console
@ -83,9 +86,7 @@ by:
$ nix develop $ nix develop
``` ```
## Testing ## Running tests
Nix comes with three different flavors of tests: unit, functional and integration.
### Unit-tests ### Unit-tests
@ -108,3 +109,72 @@ These tests include everything that needs to interact with external services or
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>). 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}` 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
sh <(curl -L <install_url>) --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

@ -1,80 +0,0 @@
# Arguments and Variables
The [Nix expression for GNU Hello](expression-syntax.md) is a
function; it is missing some arguments that have to be filled in
somewhere. In the Nix Packages collection this is done in the file
`pkgs/top-level/all-packages.nix`, where all Nix expressions for
packages are imported and called with the appropriate arguments. Here
are some fragments of `all-packages.nix`, with annotations of what
they mean:
```nix
...
rec { ①
hello = import ../applications/misc/hello/ex-1 ② { ③
inherit fetchurl stdenv perl;
};
perl = import ../development/interpreters/perl { ④
inherit fetchurl stdenv;
};
fetchurl = import ../build-support/fetchurl {
inherit stdenv; ...
};
stdenv = ...;
}
```
1. This file defines a set of attributes, all of which are concrete
derivations (i.e., not functions). In fact, we define a *mutually
recursive* set of attributes. That is, the attributes can refer to
each other. This is precisely what we want since we want to “plug”
the various packages into each other.
2. Here we *import* the Nix expression for GNU Hello. The import
operation just loads and returns the specified Nix expression. In
fact, we could just have put the contents of the Nix expression
for GNU Hello in `all-packages.nix` at this point. That would be
completely equivalent, but it would make `all-packages.nix` rather
bulky.
Note that we refer to `../applications/misc/hello/ex-1`, not
`../applications/misc/hello/ex-1/default.nix`. When you try to
import a directory, Nix automatically appends `/default.nix` to the
file name.
3. This is where the actual composition takes place. Here we *call* the
function imported from `../applications/misc/hello/ex-1` with a set
containing the things that the function expects, namely `fetchurl`,
`stdenv`, and `perl`. We use inherit again to use the attributes
defined in the surrounding scope (we could also have written
`fetchurl = fetchurl;`, etc.).
The result of this function call is an actual derivation that can be
built by Nix (since when we fill in the arguments of the function,
what we get is its body, which is the call to `stdenv.mkDerivation`
in the [Nix expression for GNU Hello](expression-syntax.md)).
> **Note**
>
> Nixpkgs has a convenience function `callPackage` that imports and
> calls a function, filling in any missing arguments by passing the
> corresponding attribute from the Nixpkgs set, like this:
>
> ```nix
> hello = callPackage ../applications/misc/hello/ex-1 { };
> ```
>
> If necessary, you can set or override arguments:
>
> ```nix
> hello = callPackage ../applications/misc/hello/ex-1 { stdenv = myStdenv; };
> ```
4. Likewise, we have to instantiate Perl, `fetchurl`, and the standard
environment.

View file

@ -1,70 +0,0 @@
# Build Script
Here is the builder referenced from Hello's Nix expression (stored in
`pkgs/applications/misc/hello/ex-1/builder.sh`):
```bash
source $stdenv/setup ①
PATH=$perl/bin:$PATH ②
tar xvfz $src ③
cd hello-*
./configure --prefix=$out ④
make ⑤
make install
```
The builder can actually be made a lot shorter by using the *generic
builder* functions provided by `stdenv`, but here we write out the build
steps to elucidate what a builder does. It performs the following steps:
1. When Nix runs a builder, it initially completely clears the
environment (except for the attributes declared in the derivation).
This is done to prevent undeclared inputs from being used in the
build process. If for example the `PATH` contained `/usr/bin`, then
you might accidentally use `/usr/bin/gcc`.
So the first step is to set up the environment. This is done by
calling the `setup` script of the standard environment. The
environment variable `stdenv` points to the location of the
standard environment being used. (It wasn't specified explicitly
as an attribute in Hello's Nix expression, but `mkDerivation` adds
it automatically.)
2. Since Hello needs Perl, we have to make sure that Perl is in the
`PATH`. The `perl` environment variable points to the location of
the Perl package (since it was passed in as an attribute to the
derivation), so `$perl/bin` is the directory containing the Perl
interpreter.
3. Now we have to unpack the sources. The `src` attribute was bound to
the result of fetching the Hello source tarball from the network, so
the `src` environment variable points to the location in the Nix
store to which the tarball was downloaded. After unpacking, we `cd`
to the resulting source directory.
The whole build is performed in a temporary directory created in
`/tmp`, by the way. This directory is removed after the builder
finishes, so there is no need to clean up the sources afterwards.
Also, the temporary directory is always newly created, so you don't
have to worry about files from previous builds interfering with the
current build.
4. GNU Hello is a typical Autoconf-based package, so we first have to
run its `configure` script. In Nix every package is stored in a
separate location in the Nix store, for instance
`/nix/store/9a54ba97fb71b65fda531012d0443ce2-hello-2.1.1`. Nix
computes this path by cryptographically hashing all attributes of
the derivation. The path is passed to the builder through the `out`
environment variable. So here we give `configure` the parameter
`--prefix=$out` to cause Hello to be installed in the expected
location.
5. Finally we build Hello (`make`) and install it into the location
specified by `out` (`make install`).
If you are wondering about the absence of error checking on the result
of various commands called in the builder: this is because the shell
script is evaluated with Bash's `-e` option, which causes the script to
be aborted if any command fails without an error check.

View file

@ -1,12 +0,0 @@
# Nix Expression Language
The Nix expression language is a pure, lazy, functional language. Purity
means that operations in the language don't have side-effects (for
instance, there is no variable assignment). Laziness means that
arguments to functions are evaluated only when they are needed.
Functional means that functions are “normal” values that can be passed
around and manipulated in interesting ways. The language is not a
full-featured, general purpose language. Its main job is to describe
packages, compositions of packages, and the variability within packages.
This section presents the various features of the language.

View file

@ -1,93 +0,0 @@
# Expression Syntax
Here is a Nix expression for GNU Hello:
```nix
{ stdenv, fetchurl, perl }: ①
stdenv.mkDerivation { ②
name = "hello-2.1.1"; ③
builder = ./builder.sh; ④
src = fetchurl { ⑤
url = "ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz";
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
};
inherit perl; ⑥
}
```
This file is actually already in the Nix Packages collection in
`pkgs/applications/misc/hello/ex-1/default.nix`. It is customary to
place each package in a separate directory and call the single Nix
expression in that directory `default.nix`. The file has the following
elements (referenced from the figure by number):
1. This states that the expression is a *function* that expects to be
called with three arguments: `stdenv`, `fetchurl`, and `perl`. They
are needed to build Hello, but we don't know how to build them here;
that's why they are function arguments. `stdenv` is a package that
is used by almost all Nix Packages; it provides a
“standard” environment consisting of the things you would expect
in a basic Unix environment: a C/C++ compiler (GCC, to be precise),
the Bash shell, fundamental Unix tools such as `cp`, `grep`, `tar`,
etc. `fetchurl` is a function that downloads files. `perl` is the
Perl interpreter.
Nix functions generally have the form `{ x, y, ..., z }: e` where
`x`, `y`, etc. are the names of the expected arguments, and where
*e* is the body of the function. So here, the entire remainder of
the file is the body of the function; when given the required
arguments, the body should describe how to build an instance of
the Hello package.
2. So we have to build a package. Building something from other stuff
is called a *derivation* in Nix (as opposed to sources, which are
built by humans instead of computers). We perform a derivation by
calling `stdenv.mkDerivation`. `mkDerivation` is a function
provided by `stdenv` that builds a package from a set of
*attributes*. A set is just a list of key/value pairs where each
key is a string and each value is an arbitrary Nix
expression. They take the general form `{ name1 = expr1; ...
nameN = exprN; }`.
3. The attribute `name` specifies the symbolic name and version of
the package. Nix doesn't really care about these things, but they
are used by for instance `nix-env -q` to show a “human-readable”
name for packages. This attribute is required by `mkDerivation`.
4. The attribute `builder` specifies the builder. This attribute can
sometimes be omitted, in which case `mkDerivation` will fill in a
default builder (which does a `configure; make; make install`, in
essence). Hello is sufficiently simple that the default builder
would suffice, but in this case, we will show an actual builder
for educational purposes. The value `./builder.sh` refers to the
shell script shown in the [next section](build-script.md),
discussed below.
5. The builder has to know what the sources of the package are. Here,
the attribute `src` is bound to the result of a call to the
`fetchurl` function. Given a URL and a SHA-256 hash of the expected
contents of the file at that URL, this function builds a derivation
that downloads the file and checks its hash. So the sources are a
dependency that like all other dependencies is built before Hello
itself is built.
Instead of `src` any other name could have been used, and in fact
there can be any number of sources (bound to different attributes).
However, `src` is customary, and it's also expected by the default
builder (which we don't use in this example).
6. Since the derivation requires Perl, we have to pass the value of the
`perl` function argument to the builder. All attributes in the set
are actually passed as environment variables to the builder, so
declaring an attribute
```nix
perl = perl;
```
will do the trick: it binds an attribute `perl` to the function
argument which also happens to be called `perl`. However, it looks a
bit silly, so there is a shorter syntax. The `inherit` keyword
causes the specified attributes to be bound to whatever variables
with the same name happen to be in scope.

View file

@ -1,66 +0,0 @@
# Generic Builder Syntax
Recall that the [build script for GNU Hello](build-script.md) looked
something like this:
```bash
PATH=$perl/bin:$PATH
tar xvfz $src
cd hello-*
./configure --prefix=$out
make
make install
```
The builders for almost all Unix packages look like this — set up some
environment variables, unpack the sources, configure, build, and
install. For this reason the standard environment provides some Bash
functions that automate the build process. Here is what a builder using
the generic build facilities looks like:
```bash
buildInputs="$perl" ①
source $stdenv/setup ②
genericBuild ③
```
Here is what each line means:
1. The `buildInputs` variable tells `setup` to use the indicated
packages as “inputs”. This means that if a package provides a `bin`
subdirectory, it's added to `PATH`; if it has a `include`
subdirectory, it's added to GCC's header search path; and so on.
(This is implemented in a modular way: `setup` tries to source the
file `pkg/nix-support/setup-hook` of all dependencies. These “setup
hooks” can then set up whatever environment variables they want; for
instance, the setup hook for Perl sets the `PERL5LIB` environment
variable to contain the `lib/site_perl` directories of all inputs.)
2. The function `genericBuild` is defined in the file `$stdenv/setup`.
3. The final step calls the shell function `genericBuild`, which
performs the steps that were done explicitly in the previous build
script. The generic builder is smart enough to figure out whether
to unpack the sources using `gzip`, `bzip2`, etc. It can be
customised in many ways; see the Nixpkgs manual for details.
Discerning readers will note that the `buildInputs` could just as well
have been set in the Nix expression, like this:
```nix
buildInputs = [ perl ];
```
The `perl` attribute can then be removed, and the builder becomes even
shorter:
```bash
source $stdenv/setup
genericBuild
```
In fact, `mkDerivation` provides a default builder that looks exactly
like that, so it is actually possible to omit the builder for Hello
entirely.

View file

@ -1,251 +0,0 @@
# Values
## Simple Values
Nix has the following basic data types:
- *Strings* can be written in three ways.
The most common way is to enclose the string between double quotes,
e.g., `"foo bar"`. Strings can span multiple lines. The special
characters `"` and `\` and the character sequence `${` must be
escaped by prefixing them with a backslash (`\`). Newlines, carriage
returns and tabs can be written as `\n`, `\r` and `\t`,
respectively.
You can include the result of an expression into a string by
enclosing it in `${...}`, a feature known as *antiquotation*. The
enclosed expression must evaluate to something that can be coerced
into a string (meaning that it must be a string, a path, or a
derivation). For instance, rather than writing
```nix
"--with-freetype2-library=" + freetype + "/lib"
```
(where `freetype` is a derivation), you can instead write the more
natural
```nix
"--with-freetype2-library=${freetype}/lib"
```
The latter is automatically translated to the former. A more
complicated example (from the Nix expression for
[Qt](http://www.trolltech.com/products/qt)):
```nix
configureFlags = "
-system-zlib -system-libpng -system-libjpeg
${if openglSupport then "-dlopen-opengl
-L${mesa}/lib -I${mesa}/include
-L${libXmu}/lib -I${libXmu}/include" else ""}
${if threadSupport then "-thread" else "-no-thread"}
";
```
Note that Nix expressions and strings can be arbitrarily nested; in
this case the outer string contains various antiquotations that
themselves contain strings (e.g., `"-thread"`), some of which in
turn contain expressions (e.g., `${mesa}`).
The second way to write string literals is as an *indented string*,
which is enclosed between pairs of *double single-quotes*, like so:
```nix
''
This is the first line.
This is the second line.
This is the third line.
''
```
This kind of string literal intelligently strips indentation from
the start of each line. To be precise, it strips from each line a
number of spaces equal to the minimal indentation of the string as a
whole (disregarding the indentation of empty lines). For instance,
the first and second line are indented two spaces, while the third
line is indented four spaces. Thus, two spaces are stripped from
each line, so the resulting string is
```nix
"This is the first line.\nThis is the second line.\n This is the third line.\n"
```
Note that the whitespace and newline following the opening `''` is
ignored if there is no non-whitespace text on the initial line.
Antiquotation (`${expr}`) is supported in indented strings.
Since `${` and `''` have special meaning in indented strings, you
need a way to quote them. `$` can be escaped by prefixing it with
`''` (that is, two single quotes), i.e., `''$`. `''` can be escaped
by prefixing it with `'`, i.e., `'''`. `$` removes any special
meaning from the following `$`. Linefeed, carriage-return and tab
characters can be written as `''\n`, `''\r`, `''\t`, and `''\`
escapes any other character.
Indented strings are primarily useful in that they allow multi-line
string literals to follow the indentation of the enclosing Nix
expression, and that less escaping is typically necessary for
strings representing languages such as shell scripts and
configuration files because `''` is much less common than `"`.
Example:
```nix
stdenv.mkDerivation {
...
postInstall =
''
mkdir $out/bin $out/etc
cp foo $out/bin
echo "Hello World" > $out/etc/foo.conf
${if enableBar then "cp bar $out/bin" else ""}
'';
...
}
```
Finally, as a convenience, *URIs* as defined in appendix B of
[RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as
is*, without quotes. For instance, the string
`"http://example.org/foo.tar.bz2"` can also be written as
`http://example.org/foo.tar.bz2`.
- Numbers, which can be *integers* (like `123`) or *floating point*
(like `123.43` or `.27e13`).
Numbers are type-compatible: pure integer operations will always
return integers, whereas any operation involving at least one
floating point number will have a floating point number as a result.
- *Paths*, e.g., `/bin/sh` or `./builder.sh`. A path must contain at
least one slash to be recognised as such. For instance, `builder.sh`
is not a path: it's parsed as an expression that selects the
attribute `sh` from the variable `builder`. If the file name is
relative, i.e., if it does not begin with a slash, it is made
absolute at parse time relative to the directory of the Nix
expression that contained it. For instance, if a Nix expression in
`/foo/bar/bla.nix` refers to `../xyzzy/fnord.nix`, the absolute path
is `/foo/xyzzy/fnord.nix`.
If the first component of a path is a `~`, it is interpreted as if
the rest of the path were relative to the user's home directory.
e.g. `~/foo` would be equivalent to `/home/edolstra/foo` for a user
whose home directory is `/home/edolstra`.
Paths can also be specified between angle brackets, e.g.
`<nixpkgs>`. This means that the directories listed in the
environment variable `NIX_PATH` will be searched for the given file
or directory name.
Antiquotation is supported in any paths except those in angle brackets.
`./${foo}-${bar}.nix` is a more convenient way of writing
`./. + "/" + foo + "-" + bar + ".nix"` or `./. + "/${foo}-${bar}.nix"`. At
least one slash must appear *before* any antiquotations for this to be
recognized as a path. `a.${foo}/b.${bar}` is a syntactically valid division
operation. `./a.${foo}/b.${bar}` is a path.
- *Booleans* with values `true` and `false`.
- The null value, denoted as `null`.
## Lists
Lists are formed by enclosing a whitespace-separated list of values
between square brackets. For example,
```nix
[ 123 ./foo.nix "abc" (f { x = y; }) ]
```
defines a list of four elements, the last being the result of a call to
the function `f`. Note that function calls have to be enclosed in
parentheses. If they had been omitted, e.g.,
```nix
[ 123 ./foo.nix "abc" f { x = y; } ]
```
the result would be a list of five elements, the fourth one being a
function and the fifth being a set.
Note that lists are only lazy in values, and they are strict in length.
## Sets
Sets are really the core of the language, since ultimately the Nix
language is all about creating derivations, which are really just sets
of attributes to be passed to build scripts.
Sets are just a list of name/value pairs (called *attributes*) enclosed
in curly brackets, where each value is an arbitrary expression
terminated by a semicolon. For example:
```nix
{ x = 123;
text = "Hello";
y = f { bla = 456; };
}
```
This defines a set with attributes named `x`, `text`, `y`. The order of
the attributes is irrelevant. An attribute name may only occur once.
Attributes can be selected from a set using the `.` operator. For
instance,
```nix
{ a = "Foo"; b = "Bar"; }.a
```
evaluates to `"Foo"`. It is possible to provide a default value in an
attribute selection using the `or` keyword. For example,
```nix
{ a = "Foo"; b = "Bar"; }.c or "Xyzzy"
```
will evaluate to `"Xyzzy"` because there is no `c` attribute in the set.
You can use arbitrary double-quoted strings as attribute names:
```nix
{ "foo ${bar}" = 123; "nix-1.0" = 456; }."foo ${bar}"
```
This will evaluate to `123` (Assuming `bar` is antiquotable). In the
case where an attribute name is just a single antiquotation, the quotes
can be dropped:
```nix
{ foo = 123; }.${bar} or 456
```
This will evaluate to `123` if `bar` evaluates to `"foo"` when coerced
to a string and `456` otherwise (again assuming `bar` is antiquotable).
In the special case where an attribute name inside of a set declaration
evaluates to `null` (which is normally an error, as `null` is not
antiquotable), that attribute is simply not added to the set:
```nix
{ ${if foo then "bar" else null} = true; }
```
This will evaluate to `{}` if `foo` evaluates to `false`.
A set that has a `__functor` attribute whose value is callable (i.e. is
itself a function or a set with a `__functor` attribute whose value is
callable) can be applied as if it were a function, with the set itself
passed in first , e.g.,
```nix
let add = { __functor = self: x: x + self.x; };
inc = add // { x = 1; };
in inc 1
```
evaluates to `2`. This can be used to attach metadata to a function
without the caller needing to treat it specially, or to implement a form
of object-oriented programming, for example.

View file

@ -1,61 +0,0 @@
# Building and Testing
You can now try to build Hello. Of course, you could do `nix-env -f . -iA
hello`, but you may not want to install a possibly broken package just
yet. The best way to test the package is by using the command
`nix-build`, which builds a Nix expression and creates a symlink named
`result` in the current directory:
```console
$ nix-build -A hello
building path `/nix/store/632d2b22514d...-hello-2.1.1'
hello-2.1.1/
hello-2.1.1/intl/
hello-2.1.1/intl/ChangeLog
...
$ ls -l result
lrwxrwxrwx ... 2006-09-29 10:43 result -> /nix/store/632d2b22514d...-hello-2.1.1
$ ./result/bin/hello
Hello, world!
```
The `-A` option selects the `hello` attribute. This is faster than
using the symbolic package name specified by the `name` attribute
(which also happens to be `hello`) and is unambiguous (there can be
multiple packages with the symbolic name `hello`, but there can be
only one attribute in a set named `hello`).
`nix-build` registers the `./result` symlink as a garbage collection
root, so unless and until you delete the `./result` symlink, the output
of the build will be safely kept on your system. You can use
`nix-build`s `-o` switch to give the symlink another name.
Nix has transactional semantics. Once a build finishes successfully, Nix
makes a note of this in its database: it registers that the path denoted
by `out` is now “valid”. If you try to build the derivation again, Nix
will see that the path is already valid and finish immediately. If a
build fails, either because it returns a non-zero exit code, because Nix
or the builder are killed, or because the machine crashes, then the
output paths will not be registered as valid. If you try to build the
derivation again, Nix will remove the output paths if they exist (e.g.,
because the builder died half-way through `make
install`) and try again. Note that there is no “negative caching”: Nix
doesn't remember that a build failed, and so a failed build can always
be repeated. This is because Nix cannot distinguish between permanent
failures (e.g., a compiler error due to a syntax error in the source)
and transient failures (e.g., a disk full condition).
Nix also performs locking. If you run multiple Nix builds
simultaneously, and they try to build the same derivation, the first Nix
instance that gets there will perform the build, while the others block
(or perform other derivations if available) until the build finishes:
```console
$ nix-build -A hello
waiting for lock on `/nix/store/0h5b7hp8d4hqfrw8igvx97x1xawrjnac-hello-2.1.1x'
```
So it is always safe to run multiple instances of Nix in parallel (which
isnt the case with, say, `make`).

View file

@ -1,23 +0,0 @@
# A Simple Nix Expression
This section shows how to add and test the [GNU Hello
package](http://www.gnu.org/software/hello/hello.html) to the Nix
Packages collection. Hello is a program that prints out the text “Hello,
world\!”.
To add a package to the Nix Packages collection, you generally need to
do three things:
1. Write a Nix expression for the package. This is a file that
describes all the inputs involved in building the package, such as
dependencies, sources, and so on.
2. Write a *builder*. This is a shell script that builds the package
from the inputs. (In fact, it can be written in any language, but
typically it's a `bash` shell script.)
3. Add the package to the file `pkgs/top-level/all-packages.nix`. The
Nix expression written in the first step is a *function*; it
requires other packages in order to build it. In this step you put
it all together, i.e., you call the function with the right
arguments to build the actual package.

View file

@ -1,12 +0,0 @@
This chapter shows you how to write Nix expressions, which instruct Nix
how to build packages. It starts with a simple example (a Nix expression
for GNU Hello), and then moves on to a more in-depth look at the Nix
expression language.
> **Note**
>
> This chapter is mostly about the Nix expression language. For more
> extensive information on adding packages to the Nix Packages
> collection (such as functions in the standard environment and coding
> conventions), please consult [its
> manual](http://nixos.org/nixpkgs/manual/).

View file

@ -3,14 +3,48 @@
- [derivation]{#gloss-derivation}\ - [derivation]{#gloss-derivation}\
A description of a build action. The result of a derivation is a A description of a build action. The result of a derivation is a
store object. Derivations are typically specified in Nix expressions store object. Derivations are typically specified in Nix expressions
using the [`derivation` primitive](expressions/derivations.md). These are using the [`derivation` primitive](language/derivations.md). These are
translated into low-level *store derivations* (implicitly by translated into low-level *store derivations* (implicitly by
`nix-env` and `nix-build`, or explicitly by `nix-instantiate`). `nix-env` and `nix-build`, or explicitly by `nix-instantiate`).
- [content-addressed derivation]{#gloss-content-addressed-derivation}\
A derivation which has the
[`__contentAddressed`](language/advanced-attributes.md#adv-attr-__contentAddressed)
attribute set to `true`.
- [fixed-output derivation]{#gloss-fixed-output-derivation}\
A derivation which includes the
[`outputHash`](language/advanced-attributes.md#adv-attr-outputHash) attribute.
- [store]{#gloss-store}\ - [store]{#gloss-store}\
The location in the file system where store objects live. Typically The location in the file system where store objects live. Typically
`/nix/store`. `/nix/store`.
From the perspective of the location where Nix is
invoked, the Nix store can be referred to
as a "_local_" or a "_remote_" one:
+ A *local store* exists on the filesystem of
the machine where Nix is invoked. You can use other
local stores by passing the `--store` flag to the
`nix` command. Local stores can be used for building derivations.
+ A *remote store* exists anywhere other than the
local filesystem. One example is the `/nix/store`
directory on another machine, accessed via `ssh` or
served by the `nix-serve` Perl script.
- [chroot store]{#gloss-chroot-store}\
A local store whose canonical path is anything other than `/nix/store`.
- [binary cache]{#gloss-binary-cache}\
A *binary cache* is a Nix store which uses a different format: its
metadata and signatures are kept in `.narinfo` files rather than in a
Nix database. This different format simplifies serving store objects
over the network, but cannot host builds. Examples of binary caches
include S3 buckets and the [NixOS binary
cache](https://cache.nixos.org).
- [store path]{#gloss-store-path}\ - [store path]{#gloss-store-path}\
The location in the file system of a store object, i.e., an The location in the file system of a store object, i.e., an
immediate child of the Nix store directory. immediate child of the Nix store directory.
@ -22,6 +56,19 @@
derivation outputs (objects produced by running a build action), or derivation outputs (objects produced by running a build action), or
derivations (files describing a build action). derivations (files describing a build action).
- [input-addressed store object]{#gloss-input-addressed-store-object}\
A store object produced by building a
non-[content-addressed](#gloss-content-addressed-derivation),
non-[fixed-output](#gloss-fixed-output-derivation)
derivation.
- [output-addressed store object]{#gloss-output-addressed-store-object}\
A store object whose store path hashes its content. This
includes derivations, the outputs of
[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
describes how to build a store object, bypassing the normal build describes how to build a store object, bypassing the normal build
@ -29,6 +76,11 @@
store object by downloading a pre-built version of the store object store object by downloading a pre-built version of the store object
from some server. from some server.
- [substituter]{#gloss-substituter}\
A *substituter* is an additional store from which Nix will
copy store objects it doesn't have. For details, see the
[`substituters` option](command-ref/conf-file.html#conf-substituters).
- [purity]{#gloss-purity}\ - [purity]{#gloss-purity}\
The assumption that equal Nix derivations when run always produce The assumption that equal Nix derivations when run always produce
the same output. This cannot be guaranteed in general (e.g., a the same output. This cannot be guaranteed in general (e.g., a

View file

@ -31,8 +31,8 @@ $ sh <(curl -L https://nixos.org/nix/install) --no-daemon
``` ```
This will perform a single-user installation of Nix, meaning that `/nix` This will perform a single-user installation of Nix, meaning that `/nix`
is owned by the invoking user. You should run this under your usual user is owned by the invoking user. You can run this under your usual user
account, *not* as root. The script will invoke `sudo` to create `/nix` account or root. The script will invoke `sudo` to create `/nix`
if it doesnt already exist. If you dont have `sudo`, you should if it doesnt already exist. If you dont have `sudo`, you should
manually create `/nix` first as root, e.g.: manually create `/nix` first as root, e.g.:
@ -71,7 +71,7 @@ $ sh <(curl -L https://nixos.org/nix/install) --daemon
The multi-user installation of Nix will create build users between the The multi-user installation of Nix will create build users between the
user IDs 30001 and 30032, and a group with the group ID 30000. You user IDs 30001 and 30032, and a group with the group ID 30000. You
should run this under your usual user account, *not* as root. The script can run this under your usual user account or root. The script
will invoke `sudo` as needed. will invoke `sudo` as needed.
> **Note** > **Note**
@ -148,7 +148,8 @@ and `/etc/zshrc` which you may remove.
This will remove all the build users that no longer serve a purpose. This will remove all the build users that no longer serve a purpose.
4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store 4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store
volume on `/nix`, which looks like this, volume on `/nix`, which looks like
`UUID=<uuid> /nix apfs rw,noauto,nobrowse,suid,owners` or
`LABEL=Nix\040Store /nix apfs rw,nobrowse`. This will prevent automatic `LABEL=Nix\040Store /nix apfs rw,nobrowse`. This will prevent automatic
mounting of the Nix Store volume. mounting of the Nix Store volume.
@ -175,6 +176,18 @@ and `/etc/zshrc` which you may remove.
This will remove the Nix Store volume and everything that was added to the This will remove the Nix Store volume and everything that was added to the
store. store.
If the output indicates that the command couldn't remove the volume, you should
make sure you don't have an _unmounted_ Nix Store volume. Look for a
"Nix Store" volume in the output of the following command:
```console
diskutil list
```
If you _do_ see a "Nix Store" volume, delete it by re-running the diskutil
deleteVolume command, but replace `/nix` with the store volume's `diskXsY`
identifier.
> **Note** > **Note**
> >
> After you complete the steps here, you will still have an empty `/nix` > After you complete the steps here, you will still have an empty `/nix`
@ -191,8 +204,7 @@ and `/etc/zshrc` which you may remove.
<!-- Note: anchors above to catch permalinks to old explanations --> <!-- Note: anchors above to catch permalinks to old explanations -->
We believe we have ironed out how to cleanly support the read-only root We believe we have ironed out how to cleanly support the read-only root
on modern macOS. New installs will do this automatically, and you can on modern macOS. New installs will do this automatically.
also re-run a new installer to convert your existing setup.
This section previously detailed the situation, options, and trade-offs, This section previously detailed the situation, options, and trade-offs,
but it now only outlines what the installer does. You don't need to know but it now only outlines what the installer does. You don't need to know

View file

@ -0,0 +1,33 @@
# Nix Language
The Nix language is
- *domain-specific*
It only exists for the Nix package manager:
to describe packages and configurations as well as their variants and compositions.
It is not intended for general purpose use.
- *declarative*
There is no notion of executing sequential steps.
Dependencies between operations are established only through data.
- *pure*
Values cannot change during computation.
Functions always produce the same output if their input does not change.
- *functional*
Functions are like any other value.
Functions can be assigned to names, taken as arguments, or returned by functions.
- *lazy*
Expressions are only evaluated when their value is needed.
- *dynamically typed*
Type errors are only detected when expressions are evaluated.

View file

@ -1,6 +1,6 @@
# Operators # Operators
The table below lists the operators in the Nix expression language, in The table below lists the operators in the Nix language, in
order of precedence (from strongest to weakest binding). order of precedence (from strongest to weakest binding).
| Name | Syntax | Associativity | Description | Precedence | | Name | Syntax | Associativity | Description | Precedence |

View file

@ -0,0 +1,275 @@
# Data Types
## Primitives
- <a id="type-string" href="#type-string">String</a>
*Strings* can be written in three ways.
The most common way is to enclose the string between double quotes,
e.g., `"foo bar"`. Strings can span multiple lines. The special
characters `"` and `\` and the character sequence `${` must be
escaped by prefixing them with a backslash (`\`). Newlines, carriage
returns and tabs can be written as `\n`, `\r` and `\t`,
respectively.
You can include the result of an expression into a string by
enclosing it in `${...}`, a feature known as *antiquotation*. The
enclosed expression must evaluate to something that can be coerced
into a string (meaning that it must be a string, a path, or a
derivation). For instance, rather than writing
```nix
"--with-freetype2-library=" + freetype + "/lib"
```
(where `freetype` is a derivation), you can instead write the more
natural
```nix
"--with-freetype2-library=${freetype}/lib"
```
The latter is automatically translated to the former. A more
complicated example (from the Nix expression for
[Qt](http://www.trolltech.com/products/qt)):
```nix
configureFlags = "
-system-zlib -system-libpng -system-libjpeg
${if openglSupport then "-dlopen-opengl
-L${mesa}/lib -I${mesa}/include
-L${libXmu}/lib -I${libXmu}/include" else ""}
${if threadSupport then "-thread" else "-no-thread"}
";
```
Note that Nix expressions and strings can be arbitrarily nested; in
this case the outer string contains various antiquotations that
themselves contain strings (e.g., `"-thread"`), some of which in
turn contain expressions (e.g., `${mesa}`).
The second way to write string literals is as an *indented string*,
which is enclosed between pairs of *double single-quotes*, like so:
```nix
''
This is the first line.
This is the second line.
This is the third line.
''
```
This kind of string literal intelligently strips indentation from
the start of each line. To be precise, it strips from each line a
number of spaces equal to the minimal indentation of the string as a
whole (disregarding the indentation of empty lines). For instance,
the first and second line are indented two spaces, while the third
line is indented four spaces. Thus, two spaces are stripped from
each line, so the resulting string is
```nix
"This is the first line.\nThis is the second line.\n This is the third line.\n"
```
Note that the whitespace and newline following the opening `''` is
ignored if there is no non-whitespace text on the initial line.
Antiquotation (`${expr}`) is supported in indented strings.
Since `${` and `''` have special meaning in indented strings, you
need a way to quote them. `$` can be escaped by prefixing it with
`''` (that is, two single quotes), i.e., `''$`. `''` can be escaped
by prefixing it with `'`, i.e., `'''`. `$` removes any special
meaning from the following `$`. Linefeed, carriage-return and tab
characters can be written as `''\n`, `''\r`, `''\t`, and `''\`
escapes any other character.
Indented strings are primarily useful in that they allow multi-line
string literals to follow the indentation of the enclosing Nix
expression, and that less escaping is typically necessary for
strings representing languages such as shell scripts and
configuration files because `''` is much less common than `"`.
Example:
```nix
stdenv.mkDerivation {
...
postInstall =
''
mkdir $out/bin $out/etc
cp foo $out/bin
echo "Hello World" > $out/etc/foo.conf
${if enableBar then "cp bar $out/bin" else ""}
'';
...
}
```
Finally, as a convenience, *URIs* as defined in appendix B of
[RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as
is*, without quotes. For instance, the string
`"http://example.org/foo.tar.bz2"` can also be written as
`http://example.org/foo.tar.bz2`.
- <a id="type-number" href="#type-number">Number</a>
Numbers, which can be *integers* (like `123`) or *floating point*
(like `123.43` or `.27e13`).
Numbers are type-compatible: pure integer operations will always
return integers, whereas any operation involving at least one
floating point number will have a floating point number as a result.
- <a id="type-path" href="#type-path">Path</a>
*Paths*, e.g., `/bin/sh` or `./builder.sh`. A path must contain at
least one slash to be recognised as such. For instance, `builder.sh`
is not a path: it's parsed as an expression that selects the
attribute `sh` from the variable `builder`. If the file name is
relative, i.e., if it does not begin with a slash, it is made
absolute at parse time relative to the directory of the Nix
expression that contained it. For instance, if a Nix expression in
`/foo/bar/bla.nix` refers to `../xyzzy/fnord.nix`, the absolute path
is `/foo/xyzzy/fnord.nix`.
If the first component of a path is a `~`, it is interpreted as if
the rest of the path were relative to the user's home directory.
e.g. `~/foo` would be equivalent to `/home/edolstra/foo` for a user
whose home directory is `/home/edolstra`.
Paths can also be specified between angle brackets, e.g.
`<nixpkgs>`. This means that the directories listed in the
environment variable `NIX_PATH` will be searched for the given file
or directory name.
Antiquotation is supported in any paths except those in angle brackets.
`./${foo}-${bar}.nix` is a more convenient way of writing
`./. + "/" + foo + "-" + bar + ".nix"` or `./. + "/${foo}-${bar}.nix"`. At
least one slash must appear *before* any antiquotations for this to be
recognized as a path. `a.${foo}/b.${bar}` is a syntactically valid division
operation. `./a.${foo}/b.${bar}` is a path.
When a path appears in an antiquotation, and is thus coerced into a string,
the path is first copied into the Nix store and the resulting string is
the Nix store path. For instance `"${./foo.txt}" will cause `foo.txt` in
the current directory to be copied into the Nix store and result in the
string `"/nix/store/<HASH>-foo.txt"`.
Note that the Nix language assumes that all input files will remain
_unchanged_ during the course of the Nix expression evaluation.
If you for example antiquote a file path during a `nix repl` session, and
then later in the same session, after having changed the file contents,
evaluate the antiquotation with the file path again, then Nix will still
return the first store path. It will _not_ reread the file contents to
produce a different Nix store path.
- <a id="type-boolean" href="#type-boolean">Boolean</a>
*Booleans* with values `true` and `false`.
- <a id="type-null" href="#type-null">Null</a>
The null value, denoted as `null`.
## List
Lists are formed by enclosing a whitespace-separated list of values
between square brackets. For example,
```nix
[ 123 ./foo.nix "abc" (f { x = y; }) ]
```
defines a list of four elements, the last being the result of a call to
the function `f`. Note that function calls have to be enclosed in
parentheses. If they had been omitted, e.g.,
```nix
[ 123 ./foo.nix "abc" f { x = y; } ]
```
the result would be a list of five elements, the fourth one being a
function and the fifth being a set.
Note that lists are only lazy in values, and they are strict in length.
## Attribute Set
An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`).
Names and values are separated by an equal sign (`=`).
Each value is an arbitrary expression terminated by a semicolon (`;`).
Attributes can appear in any order.
An attribute name may only occur once.
Example:
```nix
{
x = 123;
text = "Hello";
y = f { bla = 456; };
}
```
This defines a set with attributes named `x`, `text`, `y`.
Attributes can be selected from a set using the `.` operator. For
instance,
```nix
{ a = "Foo"; b = "Bar"; }.a
```
evaluates to `"Foo"`. It is possible to provide a default value in an
attribute selection using the `or` keyword. For example,
```nix
{ a = "Foo"; b = "Bar"; }.c or "Xyzzy"
```
will evaluate to `"Xyzzy"` because there is no `c` attribute in the set.
You can use arbitrary double-quoted strings as attribute names:
```nix
{ "foo ${bar}" = 123; "nix-1.0" = 456; }."foo ${bar}"
```
This will evaluate to `123` (Assuming `bar` is antiquotable). In the
case where an attribute name is just a single antiquotation, the quotes
can be dropped:
```nix
{ foo = 123; }.${bar} or 456
```
This will evaluate to `123` if `bar` evaluates to `"foo"` when coerced
to a string and `456` otherwise (again assuming `bar` is antiquotable).
In the special case where an attribute name inside of a set declaration
evaluates to `null` (which is normally an error, as `null` is not
antiquotable), that attribute is simply not added to the set:
```nix
{ ${if foo then "bar" else null} = true; }
```
This will evaluate to `{}` if `foo` evaluates to `false`.
A set that has a `__functor` attribute whose value is callable (i.e. is
itself a function or a set with a `__functor` attribute whose value is
callable) can be applied as if it were a function, with the set itself
passed in first , e.g.,
```nix
let add = { __functor = self: x: x + self.x; };
inc = add // { x = 1; };
in inc 1
```
evaluates to `2`. This can be used to attach metadata to a function
without the caller needing to treat it specially, or to implement a form
of object-oriented programming, for example.

View file

@ -1,5 +1,4 @@
This chapter discusses how to do package management with Nix, i.e., This chapter discusses how to do package management with Nix, i.e.,
how to obtain, install, upgrade, and erase packages. This is the how to obtain, install, upgrade, and erase packages. This is the
“users” perspective of the Nix system — people who want to *create* “users” perspective of the Nix system — people who want to *create*
packages should consult the [chapter on writing Nix packages should consult the chapter on the [Nix language](../language/index.md).
expressions](../expressions/writing-nix-expressions.md).

View file

@ -0,0 +1,5 @@
# Release 2.11 (2022-08-24)
* `nix copy` now copies the store paths in parallel as much as possible (again).
This doesn't apply for the `daemon` and `ssh-ng` stores which copy everything
in one batch to avoid latencies issues.

View file

@ -1,3 +1,8 @@
# Release X.Y (202?-??-??) # Release X.Y (202?-??-??)
* `<nix/fetchurl.nix>` now accepts an additional argument `impure` which
defaults to `false`. If it is set to `true`, the `hash` and `sha256`
arguments will be ignored and the resulting derivation will have
`__impure` set to `true`, making it an impure derivation.
* Allow explicitly selecting outputs with *store derivations* installable syntax too. * Allow explicitly selecting outputs with *store derivations* installable syntax too.

View file

@ -5,6 +5,32 @@ rec {
concatStrings = concatStringsSep ""; concatStrings = concatStringsSep "";
replaceStringsRec = from: to: string:
# recursively replace occurrences of `from` with `to` within `string`
# example:
# replaceStringRec "--" "-" "hello-----world"
# => "hello-world"
let
replaced = replaceStrings [ from ] [ to ] string;
in
if replaced == string then string else replaceStringsRec from to replaced;
squash = replaceStringsRec "\n\n\n" "\n\n";
trim = string:
# trim trailing spaces and squash non-leading spaces
let
trimLine = line:
let
# separate leading spaces from the rest
parts = split "(^ *)" line;
spaces = head (elemAt parts 1);
rest = elemAt parts 2;
# drop trailing spaces
body = head (split " *$" rest);
in spaces + replaceStringsRec " " " " body;
in concatStringsSep "\n" (map trimLine (splitLines string));
# FIXME: O(n^2) # FIXME: O(n^2)
unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) []; unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [];

View file

@ -2,10 +2,12 @@
, lib ? pkgs.lib , lib ? pkgs.lib
, name ? "nix" , name ? "nix"
, tag ? "latest" , tag ? "latest"
, bundleNixpkgs ? true
, channelName ? "nixpkgs" , channelName ? "nixpkgs"
, channelURL ? "https://nixos.org/channels/nixpkgs-unstable" , channelURL ? "https://nixos.org/channels/nixpkgs-unstable"
, extraPkgs ? [] , extraPkgs ? []
, maxLayers ? 100 , maxLayers ? 100
, nixConf ? {}
}: }:
let let
defaultPkgs = with pkgs; [ defaultPkgs = with pkgs; [
@ -31,7 +33,7 @@ let
root = { root = {
uid = 0; uid = 0;
shell = "/bin/bash"; shell = "${pkgs.bashInteractive}/bin/bash";
home = "/root"; home = "/root";
gid = 0; gid = 0;
}; };
@ -123,20 +125,27 @@ let
(lib.attrValues (lib.mapAttrs groupToGroup groups)) (lib.attrValues (lib.mapAttrs groupToGroup groups))
); );
nixConf = { defaultNixConf = {
sandbox = "false"; sandbox = "false";
build-users-group = "nixbld"; build-users-group = "nixbld";
trusted-public-keys = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="; trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
}; };
nixConfContents = (lib.concatStringsSep "\n" (lib.mapAttrsFlatten (n: v: "${n} = ${v}") nixConf)) + "\n";
nixConfContents = (lib.concatStringsSep "\n" (lib.mapAttrsFlatten (n: v:
let
vStr = if builtins.isList v then lib.concatStringsSep " " v else v;
in
"${n} = ${vStr}") (defaultNixConf // nixConf))) + "\n";
baseSystem = baseSystem =
let let
nixpkgs = pkgs.path; nixpkgs = pkgs.path;
channel = pkgs.runCommand "channel-nixos" { } '' channel = pkgs.runCommand "channel-nixos" { inherit bundleNixpkgs; } ''
mkdir $out mkdir $out
ln -s ${nixpkgs} $out/nixpkgs if [ "$bundleNixpkgs" ]; then
echo "[]" > $out/manifest.nix ln -s ${nixpkgs} $out/nixpkgs
echo "[]" > $out/manifest.nix
fi
''; '';
rootEnv = pkgs.buildPackages.buildEnv { rootEnv = pkgs.buildPackages.buildEnv {
name = "root-profile-env"; name = "root-profile-env";

View file

@ -18,11 +18,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1653988320, "lastModified": 1657693803,
"narHash": "sha256-ZaqFFsSDipZ6KVqriwM34T739+KLYJvNmCWzErjAg7c=", "narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "2fa57ed190fd6c7c746319444f34b5917666e5c1", "rev": "365e1b3a859281cf11b94f87231adeabbdd878a2",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -23,7 +23,7 @@
crossSystems = [ "armv6l-linux" "armv7l-linux" ]; crossSystems = [ "armv6l-linux" "armv7l-linux" ];
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" ]; stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
forAllSystemsAndStdenvs = f: forAllSystems (system: forAllSystemsAndStdenvs = f: forAllSystems (system:
@ -108,7 +108,7 @@
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)]; ++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
buildDeps = buildDeps =
[ curl [ (curl.override { patchNetrcRegression = true; })
bzip2 xz brotli editline bzip2 xz brotli editline
openssl sqlite openssl sqlite
libarchive libarchive
@ -260,6 +260,7 @@
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
tar cvfJ $fn \ tar cvfJ $fn \
--owner=0 --group=0 --mode=u+rw,uga+r \ --owner=0 --group=0 --mode=u+rw,uga+r \
--mtime='1970-01-01' \
--absolute-names \ --absolute-names \
--hard-dereference \ --hard-dereference \
--transform "s,$TMPDIR/install,$dir/install," \ --transform "s,$TMPDIR/install,$dir/install," \
@ -363,7 +364,7 @@
buildInputs = buildInputs =
[ nix [ nix
curl (curl.override { patchNetrcRegression = true; })
bzip2 bzip2
xz xz
pkgs.perl pkgs.perl
@ -545,6 +546,11 @@
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
} "touch $out"); } "touch $out");
installerTests = import ./tests/installer {
binaryTarballs = self.hydraJobs.binaryTarball;
inherit nixpkgsFor;
};
}; };
checks = forAllSystems (system: { checks = forAllSystems (system: {

View file

@ -10,14 +10,15 @@ function _nix() {
local -a suggestions local -a suggestions
declare -a suggestions declare -a suggestions
for suggestion in ${res:1}; do for suggestion in ${res:1}; do
# FIXME: This doesn't work properly if the suggestion word contains a `:` suggestions+=("${suggestion%% *}")
# itself
suggestions+="${suggestion/ /:}"
done done
local -a args
if [[ "$tpe" == filenames ]]; then if [[ "$tpe" == filenames ]]; then
compadd -f args+=('-f')
elif [[ "$tpe" == attrs ]]; then
args+=('-S' '')
fi fi
_describe 'nix' suggestions compadd -J nix "${args[@]}" -a suggestions
} }
_nix "$@" _nix "$@"

View file

@ -167,7 +167,7 @@ poly_user_shell_get() {
} }
poly_user_shell_set() { poly_user_shell_set() {
_sudo "in order to give $1 a safe home directory" \ _sudo "in order to give $1 a safe shell" \
/usr/bin/dscl . -create "/Users/$1" "UserShell" "$2" /usr/bin/dscl . -create "/Users/$1" "UserShell" "$2"
} }

View file

@ -37,6 +37,19 @@ readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshrc" "/e
readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix" readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
# Fish has different syntax than zsh/bash, treat it separate
readonly PROFILE_FISH_SUFFIX="conf.d/nix.fish"
readonly PROFILE_FISH_PREFIXES=(
# each of these are common values of $__fish_sysconf_dir,
# under which Fish will look for a file named
# $PROFILE_FISH_SUFFIX.
"/etc/fish" # standard
"/usr/local/etc/fish" # their installer .pkg for macOS
"/opt/homebrew/etc/fish" # homebrew
"/opt/local/etc/fish" # macports
)
readonly PROFILE_NIX_FILE_FISH="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.fish"
readonly NIX_INSTALLED_NIX="@nix@" readonly NIX_INSTALLED_NIX="@nix@"
readonly NIX_INSTALLED_CACERT="@cacert@" readonly NIX_INSTALLED_CACERT="@cacert@"
#readonly NIX_INSTALLED_NIX="/nix/store/j8dbv5w6jl34caywh2ygdy88knx1mdf7-nix-2.3.6" #readonly NIX_INSTALLED_NIX="/nix/store/j8dbv5w6jl34caywh2ygdy88knx1mdf7-nix-2.3.6"
@ -59,6 +72,30 @@ headless() {
fi fi
} }
is_root() {
if [ "$EUID" -eq 0 ]; then
return 0
else
return 1
fi
}
is_os_linux() {
if [ "$(uname -s)" = "Linux" ]; then
return 0
else
return 1
fi
}
is_os_darwin() {
if [ "$(uname -s)" = "Darwin" ]; then
return 0
else
return 1
fi
}
contact_us() { contact_us() {
echo "You can open an issue at https://github.com/nixos/nix/issues" echo "You can open an issue at https://github.com/nixos/nix/issues"
echo "" echo ""
@ -313,14 +350,23 @@ __sudo() {
_sudo() { _sudo() {
local expl="$1" local expl="$1"
shift shift
if ! headless; then if ! headless || is_root; then
__sudo "$expl" "$*" >&2 __sudo "$expl" "$*" >&2
fi fi
sudo "$@"
if is_root; then
env "$@"
else
sudo "$@"
fi
} }
# Ensure that $TMPDIR exists if defined.
if [[ -n "${TMPDIR:-}" ]] && [[ ! -d "${TMPDIR:-}" ]]; then
mkdir -m 0700 -p "${TMPDIR:-}"
fi
readonly SCRATCH=$(mktemp -d "${TMPDIR:-/tmp/}tmp.XXXXXXXXXX") readonly SCRATCH=$(mktemp -d)
finish_cleanup() { finish_cleanup() {
rm -rf "$SCRATCH" rm -rf "$SCRATCH"
} }
@ -329,7 +375,7 @@ finish_fail() {
finish_cleanup finish_cleanup
failure <<EOF failure <<EOF
Jeeze, something went wrong. If you can take all the output and open Oh no, something went wrong. If you can take all the output and open
an issue, we'd love to fix the problem so nobody else has this issue. an issue, we'd love to fix the problem so nobody else has this issue.
:( :(
@ -423,7 +469,7 @@ EOF
fi fi
done done
if [ "$(uname -s)" = "Linux" ] && [ ! -e /run/systemd/system ]; then if is_os_linux && [ ! -e /run/systemd/system ]; then
warning <<EOF warning <<EOF
We did not detect systemd on your system. With a multi-user install We did not detect systemd on your system. With a multi-user install
without systemd you will have to manually configure your init system to without systemd you will have to manually configure your init system to
@ -640,7 +686,7 @@ place_channel_configuration() {
check_selinux() { check_selinux() {
if command -v getenforce > /dev/null 2>&1; then if command -v getenforce > /dev/null 2>&1; then
if ! [ "$(getenforce)" = "Disabled" ]; then if [ "$(getenforce)" = "Enforcing" ]; then
failure <<EOF failure <<EOF
Nix does not work with selinux enabled yet! Nix does not work with selinux enabled yet!
see https://github.com/NixOS/nix/issues/2374 see https://github.com/NixOS/nix/issues/2374
@ -777,7 +823,7 @@ EOF
fi fi
_sudo "to load data for the first time in to the Nix Database" \ _sudo "to load data for the first time in to the Nix Database" \
"$NIX_INSTALLED_NIX/bin/nix-store" --load-db < ./.reginfo HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-store" --load-db < ./.reginfo
echo " Just finished getting the nix database ready." echo " Just finished getting the nix database ready."
) )
@ -795,6 +841,19 @@ fi
EOF EOF
} }
# Fish has differing syntax
fish_source_lines() {
cat <<EOF
# Nix
if test -e '$PROFILE_NIX_FILE_FISH'
. '$PROFILE_NIX_FILE_FISH'
end
# End Nix
EOF
}
configure_shell_profile() { configure_shell_profile() {
task "Setting up shell profiles: ${PROFILE_TARGETS[*]}" task "Setting up shell profiles: ${PROFILE_TARGETS[*]}"
for profile_target in "${PROFILE_TARGETS[@]}"; do for profile_target in "${PROFILE_TARGETS[@]}"; do
@ -816,6 +875,27 @@ configure_shell_profile() {
tee -a "$profile_target" tee -a "$profile_target"
fi fi
done done
task "Setting up shell profiles for Fish with with ${PROFILE_FISH_SUFFIX} inside ${PROFILE_FISH_PREFIXES[*]}"
for fish_prefix in "${PROFILE_FISH_PREFIXES[@]}"; do
if [ ! -d "$fish_prefix" ]; then
# this specific prefix (ie: /etc/fish) is very likely to exist
# if Fish is installed with this sysconfdir.
continue
fi
profile_target="${fish_prefix}/${PROFILE_FISH_SUFFIX}"
conf_dir=$(dirname "$profile_target")
if [ ! -d "$conf_dir" ]; then
_sudo "create $conf_dir for our Fish hook" \
mkdir "$conf_dir"
fi
fish_source_lines \
| _sudo "write nix-daemon settings to $profile_target" \
tee "$profile_target"
done
# TODO: should we suggest '. $PROFILE_NIX_FILE'? It would get them on # TODO: should we suggest '. $PROFILE_NIX_FILE'? It would get them on
# their way less disruptively, but a counter-argument is that they won't # their way less disruptively, but a counter-argument is that they won't
# immediately notice if something didn't get set up right? # immediately notice if something didn't get set up right?
@ -865,24 +945,14 @@ EOF
install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf
} }
main() {
# TODO: I've moved this out of validate_starting_assumptions so we
# can fail faster in this case. Sourcing install-darwin... now runs
# `touch /` to detect Read-only root, but it could update times on
# pre-Catalina macOS if run as root user.
if [ "$EUID" -eq 0 ]; then
failure <<EOF
Please do not run this script with root privileges. I will call sudo
when I need to.
EOF
fi
main() {
check_selinux check_selinux
if [ "$(uname -s)" = "Darwin" ]; then if is_os_darwin; then
# shellcheck source=./install-darwin-multi-user.sh # shellcheck source=./install-darwin-multi-user.sh
. "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh" . "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh"
elif [ "$(uname -s)" = "Linux" ]; then elif is_os_linux; then
# shellcheck source=./install-systemd-multi-user.sh # shellcheck source=./install-systemd-multi-user.sh
. "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh" # most of this works on non-systemd distros also . "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh" # most of this works on non-systemd distros also
else else
@ -890,7 +960,10 @@ EOF
fi fi
welcome_to_nix welcome_to_nix
chat_about_sudo
if ! is_root; then
chat_about_sudo
fi
cure_artifacts cure_artifacts
# TODO: there's a tension between cure and validate. I moved the # TODO: there's a tension between cure and validate. I moved the

View file

@ -209,31 +209,50 @@ if [ -z "$NIX_INSTALLER_NO_CHANNEL_ADD" ]; then
fi fi
added= added=
p=$HOME/.nix-profile/etc/profile.d/nix.sh p=
p_sh=$HOME/.nix-profile/etc/profile.d/nix.sh
p_fish=$HOME/.nix-profile/etc/profile.d/nix.fish
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
# Make the shell source nix.sh during login. # Make the shell source nix.sh during login.
for i in .bash_profile .bash_login .profile; do for i in .bash_profile .bash_login .profile; do
fn="$HOME/$i" fn="$HOME/$i"
if [ -w "$fn" ]; then if [ -w "$fn" ]; then
if ! grep -q "$p" "$fn"; then if ! grep -q "$p_sh" "$fn"; then
echo "modifying $fn..." >&2 echo "modifying $fn..." >&2
printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p" "$p" >> "$fn" printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p_sh" "$p_sh" >> "$fn"
fi fi
added=1 added=1
p=${p_sh}
break break
fi fi
done done
for i in .zshenv .zshrc; do for i in .zshenv .zshrc; do
fn="$HOME/$i" fn="$HOME/$i"
if [ -w "$fn" ]; then if [ -w "$fn" ]; then
if ! grep -q "$p" "$fn"; then if ! grep -q "$p_sh" "$fn"; then
echo "modifying $fn..." >&2 echo "modifying $fn..." >&2
printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p" "$p" >> "$fn" printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p_sh" "$p_sh" >> "$fn"
fi fi
added=1 added=1
p=${p_sh}
break break
fi fi
done done
if [ -d "$HOME/.config/fish" ]; then
fishdir=$HOME/.config/fish/conf.d
if [ ! -d "$fishdir" ]; then
mkdir -p "$fishdir"
fi
fn="$fishdir/nix.fish"
echo "placing $fn..." >&2
printf '\nif test -e %s; . %s; end # added by Nix installer\n' "$p_fish" "$p_fish" > "$fn"
added=1
p=${p_fish}
fi
else
p=${p_sh}
fi fi
if [ -z "$added" ]; then if [ -z "$added" ]; then

View file

@ -40,12 +40,12 @@ case "$(uname -s).$(uname -m)" in
path=@tarballPath_aarch64-linux@ path=@tarballPath_aarch64-linux@
system=aarch64-linux system=aarch64-linux
;; ;;
Linux.armv6l_linux) Linux.armv6l)
hash=@tarballHash_armv6l-linux@ hash=@tarballHash_armv6l-linux@
path=@tarballPath_armv6l-linux@ path=@tarballPath_armv6l-linux@
system=armv6l-linux system=armv6l-linux
;; ;;
Linux.armv7l_linux) Linux.armv7l)
hash=@tarballHash_armv7l-linux@ hash=@tarballHash_armv7l-linux@
path=@tarballPath_armv7l-linux@ path=@tarballPath_armv7l-linux@
system=armv7l-linux system=armv7l-linux

View file

@ -6,6 +6,8 @@ noinst-scripts += $(nix_noinst_scripts)
profiledir = $(sysconfdir)/profile.d profiledir = $(sysconfdir)/profile.d
$(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644)) $(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644))
$(eval $(call install-file-as, $(d)/nix-profile.fish, $(profiledir)/nix.fish, 0644))
$(eval $(call install-file-as, $(d)/nix-profile-daemon.sh, $(profiledir)/nix-daemon.sh, 0644)) $(eval $(call install-file-as, $(d)/nix-profile-daemon.sh, $(profiledir)/nix-daemon.sh, 0644))
$(eval $(call install-file-as, $(d)/nix-profile-daemon.fish, $(profiledir)/nix-daemon.fish, 0644))
clean-files += $(nix_noinst_scripts) clean-files += $(nix_noinst_scripts)

View file

@ -0,0 +1,35 @@
# Only execute this file once per shell.
if test -n "$__ETC_PROFILE_NIX_SOURCED"
exit
end
set __ETC_PROFILE_NIX_SOURCED 1
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if test -n "$NIX_SSH_CERT_FILE"
: # Allow users to override the NIX_SSL_CERT_FILE
else if test -e /etc/ssl/certs/ca-certificates.crt # NixOS, Ubuntu, Debian, Gentoo, Arch
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt
else if test -e /etc/ssl/ca-bundle.pem # openSUSE Tumbleweed
set --export NIX_SSL_CERT_FILE /etc/ssl/ca-bundle.pem
else if test -e /etc/ssl/certs/ca-bundle.crt # Old NixOS
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-bundle.crt
else if test -e /etc/pki/tls/certs/ca-bundle.crt # Fedora, CentOS
set --export NIX_SSL_CERT_FILE /etc/pki/tls/certs/ca-bundle.crt
else if test -e "$NIX_LINK/etc/ssl/certs/ca-bundle.crt" # fall back to cacert in Nix profile
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
else if test -e "$NIX_LINK/etc/ca-bundle.crt" # old cacert in Nix profile
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ca-bundle.crt"
else
# Fall back to what is in the nix profiles, favouring whatever is defined last.
for i in $NIX_PROFILES
if test -e "$i/etc/ssl/certs/ca-bundle.crt"
set --export NIX_SSL_CERT_FILE "$i/etc/ssl/certs/ca-bundle.crt"
end
end
end
fish_add_path --prepend --global "@localstatedir@/nix/profiles/default/bin"
fish_add_path --prepend --global "$HOME/.nix-profile/bin"

View file

@ -0,0 +1,37 @@
if test -n "$HOME" && test -n "$USER"
# Set up the per-user profile.
set NIX_LINK $HOME/.nix-profile
# Set up environment.
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if test -n "$NIX_SSH_CERT_FILE"
: # Allow users to override the NIX_SSL_CERT_FILE
else if test -e /etc/ssl/certs/ca-certificates.crt # NixOS, Ubuntu, Debian, Gentoo, Arch
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt
else if test -e /etc/ssl/ca-bundle.pem # openSUSE Tumbleweed
set --export NIX_SSL_CERT_FILE /etc/ssl/ca-bundle.pem
else if test -e /etc/ssl/certs/ca-bundle.crt # Old NixOS
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-bundle.crt
else if test -e /etc/pki/tls/certs/ca-bundle.crt # Fedora, CentOS
set --export NIX_SSL_CERT_FILE /etc/pki/tls/certs/ca-bundle.crt
else if test -e "$NIX_LINK/etc/ssl/certs/ca-bundle.crt" # fall back to cacert in Nix profile
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
else if test -e "$NIX_LINK/etc/ca-bundle.crt" # old cacert in Nix profile
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ca-bundle.crt"
end
# Only use MANPATH if it is already set. In general `man` will just simply
# pick up `.nix-profile/share/man` because is it close to `.nix-profile/bin`
# which is in the $PATH. For more info, run `manpath -d`.
if set --query MANPATH
set --export --prepend --path MANPATH "$NIX_LINK/share/man"
end
fish_add_path --prepend --global "$NIX_LINK/bin"
set --erase NIX_LINK
end

View file

@ -1,7 +1,6 @@
if [ -n "$HOME" ] && [ -n "$USER" ]; then if [ -n "$HOME" ] && [ -n "$USER" ]; then
# Set up the per-user profile. # Set up the per-user profile.
# This part should be kept in sync with nixpkgs:nixos/modules/programs/shell.nix
NIX_LINK=$HOME/.nix-profile NIX_LINK=$HOME/.nix-profile

View file

@ -88,7 +88,8 @@ EvalCommand::EvalCommand()
{ {
addFlag({ addFlag({
.longName = "debugger", .longName = "debugger",
.description = "start an interactive environment if evaluation fails", .description = "Start an interactive environment if evaluation fails.",
.category = MixEvalArgs::category,
.handler = {&startReplOnEvalErrors, true}, .handler = {&startReplOnEvalErrors, true},
}); });
} }
@ -225,7 +226,7 @@ MixProfile::MixProfile()
{ {
addFlag({ addFlag({
.longName = "profile", .longName = "profile",
.description = "The profile to update.", .description = "The profile to operate on.",
.labels = {"path"}, .labels = {"path"},
.handler = {&profile}, .handler = {&profile},
.completer = completePath .completer = completePath

View file

@ -13,8 +13,6 @@ namespace nix {
MixEvalArgs::MixEvalArgs() MixEvalArgs::MixEvalArgs()
{ {
auto category = "Common evaluation options";
addFlag({ addFlag({
.longName = "arg", .longName = "arg",
.description = "Pass the value *expr* as the argument *name* to Nix functions.", .description = "Pass the value *expr* as the argument *name* to Nix functions.",

View file

@ -10,6 +10,8 @@ class Bindings;
struct MixEvalArgs : virtual Args struct MixEvalArgs : virtual Args
{ {
static constexpr auto category = "Common evaluation options";
MixEvalArgs(); MixEvalArgs();
Bindings * getAutoArgs(EvalState & state); Bindings * getAutoArgs(EvalState & state);

View file

@ -628,6 +628,8 @@ InstallableFlake::InstallableFlake(
std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation() std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
{ {
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
auto attr = getCursor(*state); auto attr = getCursor(*state);
auto attrPath = attr->getAttrPathStr(); auto attrPath = attr->getAttrPathStr();

View file

@ -18,7 +18,7 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
.hmargin = 0, .hmargin = 0,
.vmargin = 0, .vmargin = 0,
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES, .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
.oflags = 0, .oflags = LOWDOWN_TERM_NOLINK,
}; };
auto doc = lowdown_doc_new(&opts); auto doc = lowdown_doc_new(&opts);

View file

@ -35,6 +35,7 @@ extern "C" {
#include "finally.hh" #include "finally.hh"
#include "markdown.hh" #include "markdown.hh"
#include "local-fs-store.hh" #include "local-fs-store.hh"
#include "progress-bar.hh"
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
#define GC_INCLUDE_NEW #define GC_INCLUDE_NEW
@ -241,7 +242,11 @@ void NixRepl::mainLoop()
// Allow nix-repl specific settings in .inputrc // Allow nix-repl specific settings in .inputrc
rl_readline_name = "nix-repl"; rl_readline_name = "nix-repl";
createDirs(dirOf(historyFile)); try {
createDirs(dirOf(historyFile));
} catch (SysError & e) {
logWarning(e.info());
}
#ifndef READLINE #ifndef READLINE
el_hist_size = 1000; el_hist_size = 1000;
#endif #endif
@ -252,6 +257,10 @@ void NixRepl::mainLoop()
rl_set_list_possib_func(listPossibleCallback); rl_set_list_possib_func(listPossibleCallback);
#endif #endif
/* Stop the progress bar because it interferes with the display of
the repl. */
stopProgressBar();
std::string input; std::string input;
while (true) { while (true) {
@ -1037,10 +1046,11 @@ void runRepl(
struct CmdRepl : InstallablesCommand struct CmdRepl : InstallablesCommand
{ {
CmdRepl(){ CmdRepl() {
evalSettings.pureEval = false; evalSettings.pureEval = false;
} }
void prepare()
void prepare() override
{ {
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) { if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) {
warn("future versions of Nix will require using `--file` to load a file"); warn("future versions of Nix will require using `--file` to load a file");
@ -1053,12 +1063,15 @@ struct CmdRepl : InstallablesCommand
} }
installables = InstallablesCommand::load(); installables = InstallablesCommand::load();
} }
std::vector<std::string> files; std::vector<std::string> files;
Strings getDefaultFlakeAttrPaths() override Strings getDefaultFlakeAttrPaths() override
{ {
return {""}; return {""};
} }
virtual bool useDefaultInstallables() override
bool useDefaultInstallables() override
{ {
return file.has_value() or expr.has_value(); return file.has_value() or expr.has_value();
} }

View file

@ -507,11 +507,6 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
return nullptr; return nullptr;
//throw TypeError("'%s' is not an attribute set", getAttrPathStr()); //throw TypeError("'%s' is not an attribute set", getAttrPathStr());
for (auto & attr : *v.attrs) {
if (root->db)
root->db->setPlaceholder({cachedValue->first, attr.name});
}
auto attr = v.attrs->get(name); auto attr = v.attrs->get(name);
if (!attr) { if (!attr) {

View file

@ -2501,18 +2501,18 @@ void EvalState::printStats()
} }
{ {
auto list = topObj.list("functions"); auto list = topObj.list("functions");
for (auto & i : functionCalls) { for (auto & [fun, count] : functionCalls) {
auto obj = list.object(); auto obj = list.object();
if (i.first->name) if (fun->name)
obj.attr("name", (const std::string &) i.first->name); obj.attr("name", (std::string_view) symbols[fun->name]);
else else
obj.attr("name", nullptr); obj.attr("name", nullptr);
if (auto pos = positions[i.first->pos]) { if (auto pos = positions[fun->pos]) {
obj.attr("file", (const std::string &) pos.file); obj.attr("file", (std::string_view) pos.file);
obj.attr("line", pos.line); obj.attr("line", pos.line);
obj.attr("column", pos.column); obj.attr("column", pos.column);
} }
obj.attr("count", i.second); obj.attr("count", count);
} }
} }
{ {

View file

@ -12,13 +12,13 @@
, executable ? false , executable ? false
, unpack ? false , unpack ? false
, name ? baseNameOf (toString url) , name ? baseNameOf (toString url)
, impure ? false
}: }:
derivation { derivation ({
builder = "builtin:fetchurl"; builder = "builtin:fetchurl";
# New-style output content requirements. # New-style output content requirements.
inherit outputHashAlgo outputHash;
outputHashMode = if unpack || executable then "recursive" else "flat"; outputHashMode = if unpack || executable then "recursive" else "flat";
inherit name url executable unpack; inherit name url executable unpack;
@ -38,4 +38,6 @@ derivation {
# To make "nix-prefetch-url" work. # To make "nix-prefetch-url" work.
urls = [ url ]; urls = [ url ];
} } // (if impure
then { __impure = true; }
else { inherit outputHashAlgo outputHash; }))

View file

@ -43,7 +43,7 @@ let
outputs = flake.outputs (inputs // { self = result; }); outputs = flake.outputs (inputs // { self = result; });
result = outputs // sourceInfo // { inherit inputs; inherit outputs; inherit sourceInfo; }; result = outputs // sourceInfo // { inherit inputs; inherit outputs; inherit sourceInfo; _type = "flake"; };
in in
if node.flake or true then if node.flake or true then
assert builtins.isFunction flake.outputs; assert builtins.isFunction flake.outputs;

View file

@ -68,7 +68,7 @@ void ConfigFile::apply()
} }
} }
if (!trusted) { if (!trusted) {
warn("ignoring untrusted flake configuration setting '%s'", name); warn("ignoring untrusted flake configuration setting '%s'.\nPass '%s' to trust it", name, "--accept-flake-config");
continue; continue;
} }
} }

View file

@ -341,7 +341,6 @@ LockedFlake lockFlake(
debug("old lock file: %s", oldLockFile); debug("old lock file: %s", oldLockFile);
// FIXME: check whether all overrides are used.
std::map<InputPath, FlakeInput> overrides; std::map<InputPath, FlakeInput> overrides;
std::set<InputPath> overridesUsed, updatesUsed; std::set<InputPath> overridesUsed, updatesUsed;
@ -484,12 +483,12 @@ LockedFlake lockFlake(
} else if (auto follows = std::get_if<1>(&i.second)) { } else if (auto follows = std::get_if<1>(&i.second)) {
if (! trustLock) { if (! trustLock) {
// It is possible that the flake has changed, // It is possible that the flake has changed,
// so we must confirm all the follows that are in the lockfile are also in the flake. // so we must confirm all the follows that are in the lock file are also in the flake.
auto overridePath(inputPath); auto overridePath(inputPath);
overridePath.push_back(i.first); overridePath.push_back(i.first);
auto o = overrides.find(overridePath); auto o = overrides.find(overridePath);
// If the override disappeared, we have to refetch the flake, // If the override disappeared, we have to refetch the flake,
// since some of the inputs may not be present in the lockfile. // since some of the inputs may not be present in the lock file.
if (o == overrides.end()) { if (o == overrides.end()) {
mustRefetch = true; mustRefetch = true;
// There's no point populating the rest of the fake inputs, // There's no point populating the rest of the fake inputs,

View file

@ -28,7 +28,7 @@ typedef std::string FlakeId;
* object that fetcher generates (usually via * object that fetcher generates (usually via
* FlakeRef::fromAttrs(attrs) or parseFlakeRef(url) calls). * FlakeRef::fromAttrs(attrs) or parseFlakeRef(url) calls).
* *
* The actual fetch not have been performed yet (i.e. a FlakeRef may * The actual fetch may not have been performed yet (i.e. a FlakeRef may
* be lazy), but the fetcher can be invoked at any time via the * be lazy), but the fetcher can be invoked at any time via the
* FlakeRef to ensure the store is populated with this input. * FlakeRef to ensure the store is populated with this input.
*/ */

View file

@ -36,7 +36,7 @@ LockedNode::LockedNode(const nlohmann::json & json)
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) , isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
{ {
if (!lockedRef.input.isLocked()) if (!lockedRef.input.isLocked())
throw Error("lockfile contains mutable lock '%s'", throw Error("lock file contains mutable lock '%s'",
fetchers::attrsToJSON(lockedRef.input.toAttrs())); fetchers::attrsToJSON(lockedRef.input.toAttrs()));
} }

View file

@ -2454,8 +2454,8 @@ static RegisterPrimOp primop_intersectAttrs({
.name = "__intersectAttrs", .name = "__intersectAttrs",
.args = {"e1", "e2"}, .args = {"e1", "e2"},
.doc = R"( .doc = R"(
Return a set consisting of the attributes in the set *e2* that also Return a set consisting of the attributes in the set *e2* which have the
exist in the set *e1*. same name as some attribute in *e1*.
)", )",
.fun = prim_intersectAttrs, .fun = prim_intersectAttrs,
}); });
@ -3821,8 +3821,8 @@ static RegisterPrimOp primop_parseDrvName({
.args = {"s"}, .args = {"s"},
.doc = R"( .doc = R"(
Split the string *s* into a package name and version. The package Split the string *s* into a package name and version. The package
name is everything up to but not including the first dash followed name is everything up to but not including the first dash not followed
by a digit, and the version is everything following that dash. The by a letter, and the version is everything following that dash. The
result is returned in a set `{ name, version }`. Thus, result is returned in a set `{ name, version }`. Thus,
`builtins.parseDrvName "nix-0.12pre12876"` returns `{ name = `builtins.parseDrvName "nix-0.12pre12876"` returns `{ name =
"nix"; version = "0.12pre12876"; }`. "nix"; version = "0.12pre12876"; }`.

View file

@ -10,7 +10,7 @@
namespace nix { namespace nix {
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context) Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context, bool copyToStore)
{ {
checkInterrupt(); checkInterrupt();
@ -32,7 +32,10 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
case nPath: case nPath:
out.write(state.copyPathToStore(context, v.path)); if (copyToStore)
out.write(state.copyPathToStore(context, v.path));
else
out.write(v.path);
break; break;
case nNull: case nNull:
@ -54,10 +57,10 @@ void printValueAsJSON(EvalState & state, bool strict,
for (auto & j : names) { for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j))); Attr & a(*v.attrs->find(state.symbols.create(j)));
auto placeholder(obj.placeholder(j)); auto placeholder(obj.placeholder(j));
printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context); printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context, copyToStore);
} }
} else } else
printValueAsJSON(state, strict, *i->value, i->pos, out, context); printValueAsJSON(state, strict, *i->value, i->pos, out, context, copyToStore);
break; break;
} }
@ -65,13 +68,13 @@ void printValueAsJSON(EvalState & state, bool strict,
auto list(out.list()); auto list(out.list());
for (auto elem : v.listItems()) { for (auto elem : v.listItems()) {
auto placeholder(list.placeholder()); auto placeholder(list.placeholder());
printValueAsJSON(state, strict, *elem, pos, placeholder, context); printValueAsJSON(state, strict, *elem, pos, placeholder, context, copyToStore);
} }
break; break;
} }
case nExternal: case nExternal:
v.external->printValueAsJSON(state, strict, out, context); v.external->printValueAsJSON(state, strict, out, context, copyToStore);
break; break;
case nFloat: case nFloat:
@ -91,14 +94,14 @@ void printValueAsJSON(EvalState & state, bool strict,
} }
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, std::ostream & str, PathSet & context) Value & v, const PosIdx pos, std::ostream & str, PathSet & context, bool copyToStore)
{ {
JSONPlaceholder out(str); JSONPlaceholder out(str);
printValueAsJSON(state, strict, v, pos, out, context); printValueAsJSON(state, strict, v, pos, out, context, copyToStore);
} }
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
JSONPlaceholder & out, PathSet & context) const JSONPlaceholder & out, PathSet & context, bool copyToStore) const
{ {
state.debugThrowLastTrace(TypeError("cannot convert %1% to JSON", showType())); state.debugThrowLastTrace(TypeError("cannot convert %1% to JSON", showType()));
} }

View file

@ -11,9 +11,9 @@ namespace nix {
class JSONPlaceholder; class JSONPlaceholder;
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context); Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context, bool copyToStore = true);
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, std::ostream & str, PathSet & context); Value & v, const PosIdx pos, std::ostream & str, PathSet & context, bool copyToStore = true);
} }

View file

@ -99,7 +99,7 @@ class ExternalValueBase
/* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */ /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */
virtual void printValueAsJSON(EvalState & state, bool strict, virtual void printValueAsJSON(EvalState & state, bool strict,
JSONPlaceholder & out, PathSet & context) const; JSONPlaceholder & out, PathSet & context, bool copyToStore = true) const;
/* Print the value as XML. Defaults to unevaluated */ /* Print the value as XML. Defaults to unevaluated */
virtual void printValueAsXML(EvalState & state, bool strict, bool location, virtual void printValueAsXML(EvalState & state, bool strict, bool location,

View file

@ -370,7 +370,7 @@ struct GitInputScheme : InputScheme
auto gitDir = ".git"; auto gitDir = ".git";
runProgram("git", true, runProgram("git", true,
{ "-C", *sourcePath, "--git-dir", gitDir, "add", "--force", "--intent-to-add", "--", std::string(file) }); { "-C", *sourcePath, "--git-dir", gitDir, "add", "--intent-to-add", "--", std::string(file) });
if (commitMsg) if (commitMsg)
runProgram("git", true, runProgram("git", true,
@ -485,6 +485,10 @@ struct GitInputScheme : InputScheme
} }
input.attrs.insert_or_assign("ref", *head); input.attrs.insert_or_assign("ref", *head);
unlockedAttrs.insert_or_assign("ref", *head); unlockedAttrs.insert_or_assign("ref", *head);
} else {
if (!input.getRev()) {
unlockedAttrs.insert_or_assign("ref", input.getRef().value());
}
} }
if (auto res = getCache()->lookup(store, unlockedAttrs)) { if (auto res = getCache()->lookup(store, unlockedAttrs)) {

View file

@ -32,6 +32,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
addFlag({ addFlag({
.longName = "option", .longName = "option",
.description = "Set the Nix configuration setting *name* to *value* (overriding `nix.conf`).", .description = "Set the Nix configuration setting *name* to *value* (overriding `nix.conf`).",
.category = miscCategory,
.labels = {"name", "value"}, .labels = {"name", "value"},
.handler = {[](std::string name, std::string value) { .handler = {[](std::string name, std::string value) {
try { try {

View file

@ -6,6 +6,7 @@ namespace nix {
//static constexpr auto commonArgsCategory = "Miscellaneous common options"; //static constexpr auto commonArgsCategory = "Miscellaneous common options";
static constexpr auto loggingCategory = "Logging-related options"; static constexpr auto loggingCategory = "Logging-related options";
static constexpr auto miscCategory = "Miscellaneous global options";
class MixCommonArgs : public virtual Args class MixCommonArgs : public virtual Args
{ {

View file

@ -30,8 +30,11 @@ Logger * makeDefaultLogger() {
return makeJSONLogger(*makeSimpleLogger(true)); return makeJSONLogger(*makeSimpleLogger(true));
case LogFormat::bar: case LogFormat::bar:
return makeProgressBar(); return makeProgressBar();
case LogFormat::barWithLogs: case LogFormat::barWithLogs: {
return makeProgressBar(true); auto logger = makeProgressBar();
logger->setPrintBuildLogs(true);
return logger;
}
default: default:
abort(); abort();
} }

View file

@ -8,6 +8,7 @@
#include <map> #include <map>
#include <thread> #include <thread>
#include <iostream> #include <iostream>
#include <chrono>
namespace nix { namespace nix {
@ -48,6 +49,7 @@ private:
bool visible = true; bool visible = true;
ActivityId parent; ActivityId parent;
std::optional<std::string> name; std::optional<std::string> name;
std::chrono::time_point<std::chrono::steady_clock> startTime;
}; };
struct ActivitiesByType struct ActivitiesByType
@ -79,22 +81,22 @@ private:
std::condition_variable quitCV, updateCV; std::condition_variable quitCV, updateCV;
bool printBuildLogs; bool printBuildLogs = false;
bool isTTY; bool isTTY;
public: public:
ProgressBar(bool printBuildLogs, bool isTTY) ProgressBar(bool isTTY)
: printBuildLogs(printBuildLogs) : isTTY(isTTY)
, isTTY(isTTY)
{ {
state_.lock()->active = isTTY; state_.lock()->active = isTTY;
updateThread = std::thread([&]() { updateThread = std::thread([&]() {
auto state(state_.lock()); auto state(state_.lock());
auto nextWakeup = std::chrono::milliseconds::max();
while (state->active) { while (state->active) {
if (!state->haveUpdate) if (!state->haveUpdate)
state.wait(updateCV); state.wait_for(updateCV, nextWakeup);
draw(*state); nextWakeup = draw(*state);
state.wait_for(quitCV, std::chrono::milliseconds(50)); state.wait_for(quitCV, std::chrono::milliseconds(50));
} }
}); });
@ -118,7 +120,8 @@ public:
updateThread.join(); updateThread.join();
} }
bool isVerbose() override { bool isVerbose() override
{
return printBuildLogs; return printBuildLogs;
} }
@ -159,11 +162,13 @@ public:
if (lvl <= verbosity && !s.empty() && type != actBuildWaiting) if (lvl <= verbosity && !s.empty() && type != actBuildWaiting)
log(*state, lvl, s + "..."); log(*state, lvl, s + "...");
state->activities.emplace_back(ActInfo()); state->activities.emplace_back(ActInfo {
.s = s,
.type = type,
.parent = parent,
.startTime = std::chrono::steady_clock::now()
});
auto i = std::prev(state->activities.end()); auto i = std::prev(state->activities.end());
i->s = s;
i->type = type;
i->parent = parent;
state->its.emplace(act, i); state->its.emplace(act, i);
state->activitiesByType[type].its.emplace(act, i); state->activitiesByType[type].its.emplace(act, i);
@ -327,10 +332,12 @@ public:
updateCV.notify_one(); updateCV.notify_one();
} }
void draw(State & state) std::chrono::milliseconds draw(State & state)
{ {
auto nextWakeup = std::chrono::milliseconds::max();
state.haveUpdate = false; state.haveUpdate = false;
if (!state.active) return; if (!state.active) return nextWakeup;
std::string line; std::string line;
@ -341,12 +348,25 @@ public:
line += "]"; line += "]";
} }
auto now = std::chrono::steady_clock::now();
if (!state.activities.empty()) { if (!state.activities.empty()) {
if (!status.empty()) line += " "; if (!status.empty()) line += " ";
auto i = state.activities.rbegin(); auto i = state.activities.rbegin();
while (i != state.activities.rend() && (!i->visible || (i->s.empty() && i->lastLine.empty()))) while (i != state.activities.rend()) {
if (i->visible && (!i->s.empty() || !i->lastLine.empty())) {
/* Don't show activities until some time has
passed, to avoid displaying very short
activities. */
auto delay = std::chrono::milliseconds(10);
if (i->startTime + delay < now)
break;
else
nextWakeup = std::min(nextWakeup, std::chrono::duration_cast<std::chrono::milliseconds>(delay - (now - i->startTime)));
}
++i; ++i;
}
if (i != state.activities.rend()) { if (i != state.activities.rend()) {
line += i->s; line += i->s;
@ -366,6 +386,8 @@ public:
if (width <= 0) width = std::numeric_limits<decltype(width)>::max(); if (width <= 0) width = std::numeric_limits<decltype(width)>::max();
writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
return nextWakeup;
} }
std::string getStatus(State & state) std::string getStatus(State & state)
@ -480,19 +502,21 @@ public:
draw(*state); draw(*state);
return s[0]; return s[0];
} }
void setPrintBuildLogs(bool printBuildLogs) override
{
this->printBuildLogs = printBuildLogs;
}
}; };
Logger * makeProgressBar(bool printBuildLogs) Logger * makeProgressBar()
{ {
return new ProgressBar( return new ProgressBar(shouldANSI());
printBuildLogs,
shouldANSI()
);
} }
void startProgressBar(bool printBuildLogs) void startProgressBar()
{ {
logger = makeProgressBar(printBuildLogs); logger = makeProgressBar();
} }
void stopProgressBar() void stopProgressBar()

View file

@ -4,9 +4,9 @@
namespace nix { namespace nix {
Logger * makeProgressBar(bool printBuildLogs = false); Logger * makeProgressBar();
void startProgressBar(bool printBuildLogs = false); void startProgressBar();
void stopProgressBar(); void stopProgressBar();

View file

@ -4,6 +4,7 @@
#include "gc-store.hh" #include "gc-store.hh"
#include "util.hh" #include "util.hh"
#include "loggers.hh" #include "loggers.hh"
#include "progress-bar.hh"
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
@ -181,8 +182,9 @@ void initNix()
/* Reset SIGCHLD to its default. */ /* Reset SIGCHLD to its default. */
struct sigaction act; struct sigaction act;
sigemptyset(&act.sa_mask); sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0; act.sa_flags = 0;
act.sa_handler = SIG_DFL;
if (sigaction(SIGCHLD, &act, 0)) if (sigaction(SIGCHLD, &act, 0))
throw SysError("resetting SIGCHLD"); throw SysError("resetting SIGCHLD");
@ -194,9 +196,20 @@ void initNix()
/* HACK: on darwin, we need cant use sigprocmask with SIGWINCH. /* HACK: on darwin, we need cant use sigprocmask with SIGWINCH.
* Instead, add a dummy sigaction handler, and signalHandlerThread * Instead, add a dummy sigaction handler, and signalHandlerThread
* can handle the rest. */ * can handle the rest. */
struct sigaction sa; act.sa_handler = sigHandler;
sa.sa_handler = sigHandler; if (sigaction(SIGWINCH, &act, 0)) throw SysError("handling SIGWINCH");
if (sigaction(SIGWINCH, &sa, 0)) throw SysError("handling SIGWINCH");
/* Disable SA_RESTART for interrupts, so that system calls on this thread
* error with EINTR like they do on Linux.
* Most signals on BSD systems default to SA_RESTART on, but Nix
* expects EINTR from syscalls to properly exit. */
act.sa_handler = SIG_DFL;
if (sigaction(SIGINT, &act, 0)) throw SysError("handling SIGINT");
if (sigaction(SIGTERM, &act, 0)) throw SysError("handling SIGTERM");
if (sigaction(SIGHUP, &act, 0)) throw SysError("handling SIGHUP");
if (sigaction(SIGPIPE, &act, 0)) throw SysError("handling SIGPIPE");
if (sigaction(SIGQUIT, &act, 0)) throw SysError("handling SIGQUIT");
if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
#endif #endif
/* Register a SIGSEGV handler to detect stack overflows. */ /* Register a SIGSEGV handler to detect stack overflows. */
@ -410,6 +423,8 @@ RunPager::RunPager()
if (!pager) pager = getenv("PAGER"); if (!pager) pager = getenv("PAGER");
if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return; if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return;
stopProgressBar();
Pipe toPager; Pipe toPager;
toPager.create(); toPager.create();

View file

@ -113,5 +113,25 @@ struct PrintFreed
/* Install a SIGSEGV handler to detect stack overflows. */ /* Install a SIGSEGV handler to detect stack overflows. */
void detectStackOverflow(); void detectStackOverflow();
/* Pluggable behavior to run in case of a stack overflow.
Default value: defaultStackOverflowHandler.
This is called by the handler installed by detectStackOverflow().
This gives Nix library consumers a limit opportunity to report the error
condition. The handler should exit the process.
See defaultStackOverflowHandler() for a reference implementation.
NOTE: Use with diligence, because this runs in the signal handler, with very
limited stack space and a potentially a corrupted heap, all while the failed
thread is blocked indefinitely. All functions called must be reentrant. */
extern std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler;
/* The default, robust implementation of stackOverflowHandler.
Prints an error message directly to stderr using a syscall instead of the
logger. Exits the process immediately after. */
void defaultStackOverflowHandler(siginfo_t * info, void * ctx);
} }

View file

@ -1,4 +1,5 @@
#include "error.hh" #include "error.hh"
#include "shared.hh"
#include <cstring> #include <cstring>
#include <cstddef> #include <cstddef>
@ -29,9 +30,7 @@ static void sigsegvHandler(int signo, siginfo_t * info, void * ctx)
ptrdiff_t diff = (char *) info->si_addr - sp; ptrdiff_t diff = (char *) info->si_addr - sp;
if (diff < 0) diff = -diff; if (diff < 0) diff = -diff;
if (diff < 4096) { if (diff < 4096) {
char msg[] = "error: stack overflow (possible infinite recursion)\n"; nix::stackOverflowHandler(info, ctx);
[[gnu::unused]] auto res = write(2, msg, strlen(msg));
_exit(1); // maybe abort instead?
} }
} }
@ -67,5 +66,12 @@ void detectStackOverflow()
#endif #endif
} }
std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler(defaultStackOverflowHandler);
void defaultStackOverflowHandler(siginfo_t * info, void * ctx) {
char msg[] = "error: stack overflow (possible infinite recursion)\n";
[[gnu::unused]] auto res = write(2, msg, strlen(msg));
_exit(1); // maybe abort instead?
}
} }

View file

@ -331,6 +331,17 @@ bool BinaryCacheStore::isValidPathUncached(const StorePath & storePath)
return fileExists(narInfoFileFor(storePath)); return fileExists(narInfoFileFor(storePath));
} }
std::optional<StorePath> BinaryCacheStore::queryPathFromHashPart(const std::string & hashPart)
{
auto pseudoPath = StorePath(hashPart + "-" + MissingName);
try {
auto info = queryPathInfo(pseudoPath);
return info->path;
} catch (InvalidPath &) {
return std::nullopt;
}
}
void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink) void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink)
{ {
auto info = queryPathInfo(storePath).cast<const NarInfo>(); auto info = queryPathInfo(storePath).cast<const NarInfo>();

View file

@ -95,8 +95,7 @@ public:
void queryPathInfoUncached(const StorePath & path, void queryPathInfoUncached(const StorePath & path,
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override; Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override;
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override;
{ unsupported("queryPathFromHashPart"); }
void addToStore(const ValidPathInfo & info, Source & narSource, void addToStore(const ValidPathInfo & info, Source & narSource,
RepairFlag repair, CheckSigsFlag checkSigs) override; RepairFlag repair, CheckSigsFlag checkSigs) override;

View file

@ -344,7 +344,7 @@ void DerivationGoal::gaveUpOnSubstitution()
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) { for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
/* Ensure that pure, non-fixed-output derivations don't /* Ensure that pure, non-fixed-output derivations don't
depend on impure derivations. */ depend on impure derivations. */
if (drv->type().isPure() && !drv->type().isFixed()) { if (settings.isExperimentalFeatureEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) {
auto inputDrv = worker.evalStore.readDerivation(i.first); auto inputDrv = worker.evalStore.readDerivation(i.first);
if (!inputDrv.type().isPure()) if (!inputDrv.type().isPure())
throw Error("pure derivation '%s' depends on impure derivation '%s'", throw Error("pure derivation '%s' depends on impure derivation '%s'",
@ -705,8 +705,7 @@ static void movePath(const Path & src, const Path & dst)
if (changePerm) if (changePerm)
chmod_(src, st.st_mode | S_IWUSR); chmod_(src, st.st_mode | S_IWUSR);
if (rename(src.c_str(), dst.c_str())) renameFile(src, dst);
throw SysError("renaming '%1%' to '%2%'", src, dst);
if (changePerm) if (changePerm)
chmod_(dst, st.st_mode); chmod_(dst, st.st_mode);
@ -914,12 +913,6 @@ void DerivationGoal::buildDone()
outputPaths outputPaths
); );
if (buildMode == bmCheck) {
cleanupPostOutputsRegisteredModeCheck();
done(BuildResult::Built, std::move(builtOutputs));
return;
}
cleanupPostOutputsRegisteredModeNonCheck(); cleanupPostOutputsRegisteredModeNonCheck();
/* Repeat the build if necessary. */ /* Repeat the build if necessary. */

View file

@ -16,11 +16,11 @@ HookInstance::HookInstance()
buildHookArgs.pop_front(); buildHookArgs.pop_front();
Strings args; Strings args;
args.push_back(std::string(baseNameOf(buildHook)));
for (auto & arg : buildHookArgs) for (auto & arg : buildHookArgs)
args.push_back(arg); args.push_back(arg);
args.push_back(std::string(baseNameOf(settings.buildHook.get())));
args.push_back(std::to_string(verbosity)); args.push_back(std::to_string(verbosity));
/* Create a pipe to get the output of the child. */ /* Create a pipe to get the output of the child. */

View file

@ -223,8 +223,7 @@ static void movePath(const Path & src, const Path & dst)
if (changePerm) if (changePerm)
chmod_(src, st.st_mode | S_IWUSR); chmod_(src, st.st_mode | S_IWUSR);
if (rename(src.c_str(), dst.c_str())) renameFile(src, dst);
throw SysError("renaming '%1%' to '%2%'", src, dst);
if (changePerm) if (changePerm)
chmod_(dst, st.st_mode); chmod_(dst, st.st_mode);
@ -311,7 +310,7 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull()
if (buildMode != bmCheck && status.known->isValid()) continue; if (buildMode != bmCheck && status.known->isValid()) continue;
auto p = worker.store.printStorePath(status.known->path); auto p = worker.store.printStorePath(status.known->path);
if (pathExists(chrootRootDir + p)) if (pathExists(chrootRootDir + p))
rename((chrootRootDir + p).c_str(), p.c_str()); renameFile((chrootRootDir + p), p);
} }
return diskFull; return diskFull;
@ -845,18 +844,43 @@ void LocalDerivationGoal::startBuilder()
/* Some distros patch Linux to not allow unprivileged /* Some distros patch Linux to not allow unprivileged
* user namespaces. If we get EPERM or EINVAL, try * user namespaces. If we get EPERM or EINVAL, try
* without CLONE_NEWUSER and see if that works. * without CLONE_NEWUSER and see if that works.
* Details: https://salsa.debian.org/kernel-team/linux/-/commit/d98e00eda6bea437e39b9e80444eee84a32438a6
*/ */
usingUserNamespace = false; usingUserNamespace = false;
flags &= ~CLONE_NEWUSER; flags &= ~CLONE_NEWUSER;
child = clone(childEntry, stack + stackSize, flags, this); child = clone(childEntry, stack + stackSize, flags, this);
} }
/* Otherwise exit with EPERM so we can handle this in the if (child == -1) {
parent. This is only done when sandbox-fallback is set switch(errno) {
to true (the default). */ case EPERM:
if (child == -1 && (errno == EPERM || errno == EINVAL) && settings.sandboxFallback) case EINVAL: {
_exit(1); int errno_ = errno;
if (child == -1) throw SysError("cloning builder process"); if (!userNamespacesEnabled && errno==EPERM)
notice("user namespaces appear to be disabled; they are required for sandboxing; check /proc/sys/user/max_user_namespaces");
if (userNamespacesEnabled) {
Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone";
if (pathExists(procSysKernelUnprivilegedUsernsClone)
&& trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0") {
notice("user namespaces appear to be disabled; they are required for sandboxing; check /proc/sys/kernel/unprivileged_userns_clone");
}
}
Path procSelfNsUser = "/proc/self/ns/user";
if (!pathExists(procSelfNsUser))
notice("/proc/self/ns/user does not exist; your kernel was likely built without CONFIG_USER_NS=y, which is required for sandboxing");
/* Otherwise exit with EPERM so we can handle this in the
parent. This is only done when sandbox-fallback is set
to true (the default). */
if (settings.sandboxFallback)
_exit(1);
/* Mention sandbox-fallback in the error message so the user
knows that having it disabled contributed to the
unrecoverability of this failure */
throw SysError(errno_, "creating sandboxed builder process using clone(), without sandbox-fallback");
}
default:
throw SysError("creating sandboxed builder process using clone()");
}
}
writeFull(builderOut.writeSide.get(), writeFull(builderOut.writeSide.get(),
fmt("%d %d\n", usingUserNamespace, child)); fmt("%d %d\n", usingUserNamespace, child));
_exit(0); _exit(0);
@ -1570,6 +1594,8 @@ void LocalDerivationGoal::runChild()
/* Warning: in the child we should absolutely not make any SQLite /* Warning: in the child we should absolutely not make any SQLite
calls! */ calls! */
bool sendException = true;
try { /* child */ try { /* child */
commonChildInit(builderOut); commonChildInit(builderOut);
@ -2026,6 +2052,8 @@ void LocalDerivationGoal::runChild()
/* Indicate that we managed to set up the build environment. */ /* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n")); writeFull(STDERR_FILENO, std::string("\2\n"));
sendException = false;
/* Execute the program. This should not return. */ /* Execute the program. This should not return. */
if (drv->isBuiltin()) { if (drv->isBuiltin()) {
try { try {
@ -2079,10 +2107,13 @@ void LocalDerivationGoal::runChild()
throw SysError("executing '%1%'", drv->builder); throw SysError("executing '%1%'", drv->builder);
} catch (Error & e) { } catch (Error & e) {
writeFull(STDERR_FILENO, "\1\n"); if (sendException) {
FdSink sink(STDERR_FILENO); writeFull(STDERR_FILENO, "\1\n");
sink << e; FdSink sink(STDERR_FILENO);
sink.flush(); sink << e;
sink.flush();
} else
std::cerr << e.msg();
_exit(1); _exit(1);
} }
} }
@ -2350,10 +2381,8 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
if (*scratchPath != finalPath) { if (*scratchPath != finalPath) {
// Also rewrite the output path // Also rewrite the output path
auto source = sinkToSource([&](Sink & nextSink) { auto source = sinkToSource([&](Sink & nextSink) {
StringSink sink;
dumpPath(actualPath, sink);
RewritingSink rsink2(oldHashPart, std::string(finalPath.hashPart()), nextSink); RewritingSink rsink2(oldHashPart, std::string(finalPath.hashPart()), nextSink);
rsink2(sink.s); dumpPath(actualPath, rsink2);
rsink2.flush(); rsink2.flush();
}); });
Path tmpPath = actualPath + ".tmp"; Path tmpPath = actualPath + ".tmp";
@ -2600,8 +2629,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
Path prev = path + checkSuffix; Path prev = path + checkSuffix;
deletePath(prev); deletePath(prev);
Path dst = path + checkSuffix; Path dst = path + checkSuffix;
if (rename(path.c_str(), dst.c_str())) renameFile(path, dst);
throw SysError("renaming '%s' to '%s'", path, dst);
} }
} }

View file

@ -22,8 +22,7 @@ void builtinUnpackChannel(const BasicDerivation & drv)
auto entries = readDirectory(out); auto entries = readDirectory(out);
if (entries.size() != 1) if (entries.size() != 1)
throw Error("channel tarball '%s' contains more than one file", src); throw Error("channel tarball '%s' contains more than one file", src);
if (rename((out + "/" + entries[0].name).c_str(), (out + "/" + channelName).c_str()) == -1) renameFile((out + "/" + entries[0].name), (out + "/" + channelName));
throw SysError("renaming channel directory");
} }
} }

View file

@ -239,6 +239,8 @@ struct ClientSettings
else if (trusted else if (trusted
|| name == settings.buildTimeout.name || name == settings.buildTimeout.name
|| name == settings.buildRepeat.name || name == settings.buildRepeat.name
|| name == settings.maxSilentTime.name
|| name == settings.pollInterval.name
|| name == "connect-timeout" || name == "connect-timeout"
|| (name == "builders" && value == "")) || (name == "builders" && value == ""))
settings.set(name, value); settings.set(name, value);

View file

@ -308,6 +308,9 @@ struct curlFileTransfer : public FileTransfer
curl_easy_setopt(req, CURLOPT_HTTPHEADER, requestHeaders); curl_easy_setopt(req, CURLOPT_HTTPHEADER, requestHeaders);
if (settings.downloadSpeed.get() > 0)
curl_easy_setopt(req, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) (settings.downloadSpeed.get() * 1024));
if (request.head) if (request.head)
curl_easy_setopt(req, CURLOPT_NOBODY, 1); curl_easy_setopt(req, CURLOPT_NOBODY, 1);
@ -319,7 +322,6 @@ struct curlFileTransfer : public FileTransfer
} }
if (request.verifyTLS) { if (request.verifyTLS) {
debug("verify TLS: Nix CA file = '%s'", settings.caFile);
if (settings.caFile != "") if (settings.caFile != "")
curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.c_str()); curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.c_str());
} else { } else {

View file

@ -39,9 +39,7 @@ static void makeSymlink(const Path & link, const Path & target)
createSymlink(target, tempLink); createSymlink(target, tempLink);
/* Atomically replace the old one. */ /* Atomically replace the old one. */
if (rename(tempLink.c_str(), link.c_str()) == -1) renameFile(tempLink, link);
throw SysError("cannot rename '%1%' to '%2%'",
tempLink , link);
} }
@ -621,6 +619,17 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
Path path = storeDir + "/" + std::string(baseName); Path path = storeDir + "/" + std::string(baseName);
Path realPath = realStoreDir + "/" + std::string(baseName); Path realPath = realStoreDir + "/" + std::string(baseName);
/* There may be temp directories in the store that are still in use
by another process. We need to be sure that we can acquire an
exclusive lock before deleting them. */
if (baseName.find("tmp-", 0) == 0) {
AutoCloseFD tmpDirFd = open(realPath.c_str(), O_RDONLY | O_DIRECTORY);
if (tmpDirFd.get() == -1 || !lockFile(tmpDirFd.get(), ltWrite, false)) {
debug("skipping locked tempdir '%s'", realPath);
return;
}
}
printInfo("deleting '%1%'", path); printInfo("deleting '%1%'", path);
results.paths.insert(path); results.paths.insert(path);

View file

@ -114,7 +114,13 @@ std::vector<Path> getUserConfigFiles()
unsigned int Settings::getDefaultCores() unsigned int Settings::getDefaultCores()
{ {
return std::max(1U, std::thread::hardware_concurrency()); const unsigned int concurrency = std::max(1U, std::thread::hardware_concurrency());
const unsigned int maxCPU = getMaxCPU();
if (maxCPU > 0)
return maxCPU;
else
return concurrency;
} }
StringSet Settings::getDefaultSystemFeatures() StringSet Settings::getDefaultSystemFeatures()
@ -148,13 +154,9 @@ StringSet Settings::getDefaultExtraPlatforms()
// machines. Note that we cant force processes from executing // machines. Note that we cant force processes from executing
// x86_64 in aarch64 environments or vice versa since they can // x86_64 in aarch64 environments or vice versa since they can
// always exec with their own binary preferences. // always exec with their own binary preferences.
if (pathExists("/Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist") || if (std::string{SYSTEM} == "aarch64-darwin" &&
pathExists("/System/Library/LaunchDaemons/com.apple.oahd.plist")) { runProgram(RunOptions {.program = "arch", .args = {"-arch", "x86_64", "/usr/bin/true"}, .mergeStderrToStdout = true}).first == 0)
if (std::string{SYSTEM} == "x86_64-darwin") extraPlatforms.insert("x86_64-darwin");
extraPlatforms.insert("aarch64-darwin");
else if (std::string{SYSTEM} == "aarch64-darwin")
extraPlatforms.insert("x86_64-darwin");
}
#endif #endif
return extraPlatforms; return extraPlatforms;

View file

@ -560,9 +560,15 @@ public:
R"( R"(
If set to `true` (the default), any non-content-addressed path added If set to `true` (the default), any non-content-addressed path added
or copied to the Nix store (e.g. when substituting from a binary or copied to the Nix store (e.g. when substituting from a binary
cache) must have a valid signature, that is, be signed using one of cache) must have a signature by a trusted key. A trusted key is one
the keys listed in `trusted-public-keys` or `secret-key-files`. Set listed in `trusted-public-keys`, or a public key counterpart to a
to `false` to disable signature checking. private key stored in a file listed in `secret-key-files`.
Set to `false` to disable signature checking and trust all
non-content-addressed paths unconditionally.
(Content-addressed paths are inherently trustworthy and thus
unaffected by this configuration option.)
)"}; )"};
Setting<StringSet> extraPlatforms{ Setting<StringSet> extraPlatforms{
@ -613,6 +619,14 @@ public:
are tried based on their Priority value, which each substituter can set are tried based on their Priority value, which each substituter can set
independently. Lower value means higher priority. independently. Lower value means higher priority.
The default is `https://cache.nixos.org`, with a Priority of 40. The default is `https://cache.nixos.org`, with a Priority of 40.
Nix will copy a store path from a remote store only if one
of the following is true:
- the store object is signed by one of the [`trusted-public-keys`](#conf-trusted-public-keys)
- the substituter is in the [`trusted-substituters`](#conf-trusted-substituters) list
- the [`require-sigs`](#conf-require-sigs) option has been set to `false`
- the store object is [output-addressed](glossary.md#gloss-output-addressed-store-object)
)", )",
{"binary-caches"}}; {"binary-caches"}};
@ -746,6 +760,13 @@ public:
/nix/store/xfghy8ixrhz3kyy6p724iv3cxji088dx-bash-4.4-p23`. /nix/store/xfghy8ixrhz3kyy6p724iv3cxji088dx-bash-4.4-p23`.
)"}; )"};
Setting<unsigned int> downloadSpeed {
this, 0, "download-speed",
R"(
Specify the maximum transfer rate in kilobytes per second you want
Nix to use for downloads.
)"};
Setting<std::string> netrcFile{ Setting<std::string> netrcFile{
this, fmt("%s/%s", nixConfDir, "netrc"), "netrc-file", this, fmt("%s/%s", nixConfDir, "netrc"), "netrc-file",
R"( R"(

View file

@ -57,8 +57,7 @@ protected:
AutoDelete del(tmp, false); AutoDelete del(tmp, false);
StreamToSourceAdapter source(istream); StreamToSourceAdapter source(istream);
writeFile(tmp, source); writeFile(tmp, source);
if (rename(tmp.c_str(), path2.c_str())) renameFile(tmp, path2);
throw SysError("renaming '%1%' to '%2%'", tmp, path2);
del.cancel(); del.cancel();
} }

View file

@ -158,7 +158,7 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
txn.commit(); txn.commit();
} }
writeFile(schemaPath, fmt("%d", nixCASchemaVersion)); writeFile(schemaPath, fmt("%d", nixCASchemaVersion), 0666, true);
lockFile(lockFd.get(), ltRead, true); lockFile(lockFd.get(), ltRead, true);
} }
} }
@ -281,7 +281,7 @@ LocalStore::LocalStore(const Params & params)
else if (curSchema == 0) { /* new store */ else if (curSchema == 0) { /* new store */
curSchema = nixSchemaVersion; curSchema = nixSchemaVersion;
openDB(*state, true); openDB(*state, true);
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str(), 0666, true);
} }
else if (curSchema < nixSchemaVersion) { else if (curSchema < nixSchemaVersion) {
@ -329,7 +329,7 @@ LocalStore::LocalStore(const Params & params)
txn.commit(); txn.commit();
} }
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str(), 0666, true);
lockFile(globalLock.get(), ltRead, true); lockFile(globalLock.get(), ltRead, true);
} }
@ -751,7 +751,7 @@ void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag check
if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info)) if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info))
registerDrvOutput(info); registerDrvOutput(info);
else else
throw Error("cannot register realisation '%s' because it lacks a valid signature", info.outPath.to_string()); throw Error("cannot register realisation '%s' because it lacks a signature by a trusted key", info.outPath.to_string());
} }
void LocalStore::registerDrvOutput(const Realisation & info) void LocalStore::registerDrvOutput(const Realisation & info)
@ -1266,7 +1266,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
RepairFlag repair, CheckSigsFlag checkSigs) RepairFlag repair, CheckSigsFlag checkSigs)
{ {
if (checkSigs && pathInfoIsUntrusted(info)) if (checkSigs && pathInfoIsUntrusted(info))
throw Error("cannot add path '%s' because it lacks a valid signature", printStorePath(info.path)); throw Error("cannot add path '%s' because it lacks a signature by a trusted key", printStorePath(info.path));
addTempRoot(info.path); addTempRoot(info.path);
@ -1382,13 +1382,15 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name
std::unique_ptr<AutoDelete> delTempDir; std::unique_ptr<AutoDelete> delTempDir;
Path tempPath; Path tempPath;
Path tempDir;
AutoCloseFD tempDirFd;
if (!inMemory) { if (!inMemory) {
/* Drain what we pulled so far, and then keep on pulling */ /* Drain what we pulled so far, and then keep on pulling */
StringSource dumpSource { dump }; StringSource dumpSource { dump };
ChainSource bothSource { dumpSource, source }; ChainSource bothSource { dumpSource, source };
auto tempDir = createTempDir(realStoreDir, "add"); std::tie(tempDir, tempDirFd) = createTempDirInStore();
delTempDir = std::make_unique<AutoDelete>(tempDir); delTempDir = std::make_unique<AutoDelete>(tempDir);
tempPath = tempDir + "/x"; tempPath = tempDir + "/x";
@ -1430,8 +1432,7 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name
writeFile(realPath, dumpSource); writeFile(realPath, dumpSource);
} else { } else {
/* Move the temporary path we restored above. */ /* Move the temporary path we restored above. */
if (rename(tempPath.c_str(), realPath.c_str())) moveFile(tempPath, realPath);
throw Error("renaming '%s' to '%s'", tempPath, realPath);
} }
/* For computing the nar hash. In recursive SHA-256 mode, this /* For computing the nar hash. In recursive SHA-256 mode, this
@ -1508,18 +1509,24 @@ StorePath LocalStore::addTextToStore(
/* Create a temporary directory in the store that won't be /* Create a temporary directory in the store that won't be
garbage-collected. */ garbage-collected until the returned FD is closed. */
Path LocalStore::createTempDirInStore() std::pair<Path, AutoCloseFD> LocalStore::createTempDirInStore()
{ {
Path tmpDir; Path tmpDirFn;
AutoCloseFD tmpDirFd;
bool lockedByUs = false;
do { do {
/* There is a slight possibility that `tmpDir' gets deleted by /* There is a slight possibility that `tmpDir' gets deleted by
the GC between createTempDir() and addTempRoot(), so repeat the GC between createTempDir() and when we acquire a lock on it.
until `tmpDir' exists. */ We'll repeat until 'tmpDir' exists and we've locked it. */
tmpDir = createTempDir(realStoreDir); tmpDirFn = createTempDir(realStoreDir, "tmp");
addTempRoot(parseStorePath(tmpDir)); tmpDirFd = open(tmpDirFn.c_str(), O_RDONLY | O_DIRECTORY);
} while (!pathExists(tmpDir)); if (tmpDirFd.get() < 0) {
return tmpDir; continue;
}
lockedByUs = lockFile(tmpDirFd.get(), ltWrite, true);
} while (!pathExists(tmpDirFn) || !lockedByUs);
return {tmpDirFn, std::move(tmpDirFd)};
} }
@ -1942,8 +1949,7 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)
writeFile(tmpFile, compress("bzip2", log)); writeFile(tmpFile, compress("bzip2", log));
if (rename(tmpFile.c_str(), logPath.c_str()) != 0) renameFile(tmpFile, logPath);
throw SysError("renaming '%1%' to '%2%'", tmpFile, logPath);
} }
std::optional<std::string> LocalStore::getVersion() std::optional<std::string> LocalStore::getVersion()

View file

@ -256,7 +256,7 @@ private:
void findRuntimeRoots(Roots & roots, bool censor); void findRuntimeRoots(Roots & roots, bool censor);
Path createTempDirInStore(); std::pair<Path, AutoCloseFD> createTempDirInStore();
void checkDerivationOutputs(const StorePath & drvPath, const Derivation & drv); void checkDerivationOutputs(const StorePath & drvPath, const Derivation & drv);

View file

@ -75,6 +75,9 @@ struct NarAccessor : public FSAccessor
createMember(path, {FSAccessor::Type::tRegular, false, 0, 0}); createMember(path, {FSAccessor::Type::tRegular, false, 0, 0});
} }
void closeRegularFile() override
{ }
void isExecutable() override void isExecutable() override
{ {
parents.top()->isExecutable = true; parents.top()->isExecutable = true;

View file

@ -229,7 +229,9 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
} }
/* Atomically replace the old file with the new hard link. */ /* Atomically replace the old file with the new hard link. */
if (rename(tempLink.c_str(), path.c_str()) == -1) { try {
renameFile(tempLink, path);
} catch (SysError & e) {
if (unlink(tempLink.c_str()) == -1) if (unlink(tempLink.c_str()) == -1)
printError("unable to unlink '%1%'", tempLink); printError("unable to unlink '%1%'", tempLink);
if (errno == EMLINK) { if (errno == EMLINK) {
@ -240,7 +242,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
debug("'%s' has reached maximum number of links", linkPath); debug("'%s' has reached maximum number of links", linkPath);
return; return;
} }
throw SysError("cannot rename '%1%' to '%2%'", tempLink, path); throw;
} }
stats.filesLinked++; stats.filesLinked++;

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