forked from lix-project/lix
Merge remote-tracking branch 'origin/master' into cross-jobs
This commit is contained in:
commit
c3a929349f
2
.github/STALE-BOT.md
vendored
2
.github/STALE-BOT.md
vendored
|
@ -3,7 +3,7 @@
|
|||
- Thanks for your contribution!
|
||||
- To remove the stale label, just leave a new comment.
|
||||
- _How to find the right people to ping?_ → [`git blame`](https://git-scm.com/docs/git-blame) to the rescue! (or GitHub's history and blame buttons.)
|
||||
- You can always ask for help on [our Discourse Forum](https://discourse.nixos.org/) or on the [#nixos IRC channel](https://webchat.freenode.net/#nixos).
|
||||
- You can always ask for help on [our Discourse Forum](https://discourse.nixos.org/) or on [Matrix - #nix:nixos.org](https://matrix.to/#/#nix:nixos.org).
|
||||
|
||||
## Suggestions for PRs
|
||||
|
||||
|
|
44
.github/workflows/test.yml
vendored
44
.github/workflows/test.yml
vendored
|
@ -8,52 +8,62 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CACHIX_NAME: nix-ci
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v12
|
||||
- uses: cachix/cachix-action@v8
|
||||
- uses: cachix/install-nix-action@v13
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v10
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
#- run: nix flake check
|
||||
- run: nix-build -A checks.$(if [[ `uname` = Linux ]]; then echo x86_64-linux; else echo x86_64-darwin; fi)
|
||||
installer:
|
||||
if: github.event_name == 'push'
|
||||
needs: tests
|
||||
check_cachix:
|
||||
name: Cachix secret present for installer tests
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
secret: ${{ steps.secret.outputs.secret }}
|
||||
steps:
|
||||
- name: Check for Cachix secret
|
||||
id: secret
|
||||
env:
|
||||
CACHIX_NAME: nix-ci
|
||||
_CACHIX_SECRETS: ${{ secrets.CACHIX_SIGNING_KEY }}${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
run: echo "::set-output name=secret::${{ env._CACHIX_SECRETS != '' }}"
|
||||
installer:
|
||||
needs: [tests, check_cachix]
|
||||
if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v12
|
||||
- uses: cachix/cachix-action@v8
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v13
|
||||
- uses: cachix/cachix-action@v10
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- id: prepare-installer
|
||||
run: scripts/prepare-installer-for-github-actions
|
||||
installer_test:
|
||||
if: github.event_name == 'push'
|
||||
needs: installer
|
||||
needs: [installer, check_cachix]
|
||||
if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true'
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CACHIX_NAME: nix-ci
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: cachix/install-nix-action@master
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v13
|
||||
with:
|
||||
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
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -82,6 +82,7 @@ perl/Makefile.config
|
|||
/tests/shell
|
||||
/tests/shell.drv
|
||||
/tests/config.nix
|
||||
/tests/ca/config.nix
|
||||
|
||||
# /tests/lang/
|
||||
/tests/lang/*.out
|
||||
|
|
1
Makefile
1
Makefile
|
@ -12,6 +12,7 @@ makefiles = \
|
|||
src/resolve-system-dependencies/local.mk \
|
||||
scripts/local.mk \
|
||||
misc/bash/local.mk \
|
||||
misc/zsh/local.mk \
|
||||
misc/systemd/local.mk \
|
||||
misc/launchd/local.mk \
|
||||
misc/upstart/local.mk \
|
||||
|
|
|
@ -15,7 +15,6 @@ LDFLAGS = @LDFLAGS@
|
|||
LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
|
||||
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
|
||||
LIBCURL_LIBS = @LIBCURL_LIBS@
|
||||
LIBLZMA_LIBS = @LIBLZMA_LIBS@
|
||||
OPENSSL_LIBS = @OPENSSL_LIBS@
|
||||
LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
|
|
|
@ -28,7 +28,8 @@ build nix from source with nix-build or how to get a development environment.
|
|||
- [Nix manual](https://nixos.org/nix/manual)
|
||||
- [Nix jobsets on hydra.nixos.org](https://hydra.nixos.org/project/nix)
|
||||
- [NixOS Discourse](https://discourse.nixos.org/)
|
||||
- [IRC - #nixos on freenode.net](irc://irc.freenode.net/#nixos)
|
||||
- [Matrix - #nix:nixos.org](https://matrix.to/#/#nix:nixos.org)
|
||||
- [IRC - #nixos on libera.chat](irc://irc.libera.chat/#nixos)
|
||||
|
||||
## License
|
||||
|
||||
|
|
20
config/config.guess
vendored
20
config/config.guess
vendored
|
@ -1,8 +1,8 @@
|
|||
#! /bin/sh
|
||||
# Attempt to guess a canonical system name.
|
||||
# Copyright 1992-2020 Free Software Foundation, Inc.
|
||||
# Copyright 1992-2021 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2020-11-19'
|
||||
timestamp='2021-01-25'
|
||||
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
|
@ -50,7 +50,7 @@ version="\
|
|||
GNU config.guess ($timestamp)
|
||||
|
||||
Originally written by Per Bothner.
|
||||
Copyright 1992-2020 Free Software Foundation, Inc.
|
||||
Copyright 1992-2021 Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
|
@ -188,10 +188,9 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
|
|||
#
|
||||
# Note: NetBSD doesn't particularly care about the vendor
|
||||
# portion of the name. We always set it to "unknown".
|
||||
sysctl="sysctl -n hw.machine_arch"
|
||||
UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \
|
||||
"/sbin/$sysctl" 2>/dev/null || \
|
||||
"/usr/sbin/$sysctl" 2>/dev/null || \
|
||||
/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
|
||||
/usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
|
||||
echo unknown))
|
||||
case "$UNAME_MACHINE_ARCH" in
|
||||
aarch64eb) machine=aarch64_be-unknown ;;
|
||||
|
@ -996,6 +995,9 @@ EOF
|
|||
k1om:Linux:*:*)
|
||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||
exit ;;
|
||||
loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
|
||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||
exit ;;
|
||||
m32r*:Linux:*:*)
|
||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||
exit ;;
|
||||
|
@ -1084,7 +1086,7 @@ EOF
|
|||
ppcle:Linux:*:*)
|
||||
echo powerpcle-unknown-linux-"$LIBC"
|
||||
exit ;;
|
||||
riscv32:Linux:*:* | riscv64:Linux:*:*)
|
||||
riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
|
||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||
exit ;;
|
||||
s390:Linux:*:* | s390x:Linux:*:*)
|
||||
|
@ -1480,8 +1482,8 @@ EOF
|
|||
i*86:rdos:*:*)
|
||||
echo "$UNAME_MACHINE"-pc-rdos
|
||||
exit ;;
|
||||
i*86:AROS:*:*)
|
||||
echo "$UNAME_MACHINE"-pc-aros
|
||||
*:AROS:*:*)
|
||||
echo "$UNAME_MACHINE"-unknown-aros
|
||||
exit ;;
|
||||
x86_64:VMkernel:*:*)
|
||||
echo "$UNAME_MACHINE"-unknown-esx
|
||||
|
|
20
config/config.sub
vendored
20
config/config.sub
vendored
|
@ -1,8 +1,8 @@
|
|||
#! /bin/sh
|
||||
# Configuration validation subroutine script.
|
||||
# Copyright 1992-2020 Free Software Foundation, Inc.
|
||||
# Copyright 1992-2021 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2020-12-02'
|
||||
timestamp='2021-01-08'
|
||||
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
|
@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
|
|||
version="\
|
||||
GNU config.sub ($timestamp)
|
||||
|
||||
Copyright 1992-2020 Free Software Foundation, Inc.
|
||||
Copyright 1992-2021 Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
|
@ -1185,6 +1185,7 @@ case $cpu-$vendor in
|
|||
| k1om \
|
||||
| le32 | le64 \
|
||||
| lm32 \
|
||||
| loongarch32 | loongarch64 | loongarchx32 \
|
||||
| m32c | m32r | m32rle \
|
||||
| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
|
||||
| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
|
||||
|
@ -1229,7 +1230,7 @@ case $cpu-$vendor in
|
|||
| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
|
||||
| pru \
|
||||
| pyramid \
|
||||
| riscv | riscv32 | riscv64 \
|
||||
| riscv | riscv32 | riscv32be | riscv64 | riscv64be \
|
||||
| rl78 | romp | rs6000 | rx \
|
||||
| s390 | s390x \
|
||||
| score \
|
||||
|
@ -1682,11 +1683,14 @@ fi
|
|||
|
||||
# Now, validate our (potentially fixed-up) OS.
|
||||
case $os in
|
||||
# Sometimes we do "kernel-abi", so those need to count as OSes.
|
||||
# Sometimes we do "kernel-libc", so those need to count as OSes.
|
||||
musl* | newlib* | uclibc*)
|
||||
;;
|
||||
# Likewise for "kernel-libc"
|
||||
eabi | eabihf | gnueabi | gnueabihf)
|
||||
# Likewise for "kernel-abi"
|
||||
eabi* | gnueabi*)
|
||||
;;
|
||||
# VxWorks passes extra cpu info in the 4th filed.
|
||||
simlinux | simwindows | spe)
|
||||
;;
|
||||
# Now accept the basic system types.
|
||||
# The portable systems comes first.
|
||||
|
@ -1750,6 +1754,8 @@ case $kernel-$os in
|
|||
;;
|
||||
kfreebsd*-gnu* | kopensolaris*-gnu*)
|
||||
;;
|
||||
vxworks-simlinux | vxworks-simwindows | vxworks-spe)
|
||||
;;
|
||||
nto-qnx*)
|
||||
;;
|
||||
os2-emx)
|
||||
|
|
61
configure.ac
61
configure.ac
|
@ -1,4 +1,4 @@
|
|||
AC_INIT(nix, m4_esyscmd([bash -c "echo -n $(cat ./.version)$VERSION_SUFFIX"]))
|
||||
AC_INIT([nix],[m4_esyscmd(bash -c "echo -n $(cat ./.version)$VERSION_SUFFIX")])
|
||||
AC_CONFIG_MACRO_DIRS([m4])
|
||||
AC_CONFIG_SRCDIR(README.md)
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
|
@ -9,8 +9,7 @@ AC_PROG_SED
|
|||
AC_CANONICAL_HOST
|
||||
AC_MSG_CHECKING([for the canonical Nix system name])
|
||||
|
||||
AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM],
|
||||
[Platform identifier (e.g., `i686-linux').]),
|
||||
AC_ARG_WITH(system, AS_HELP_STRING([--with-system=SYSTEM],[Platform identifier (e.g., `i686-linux').]),
|
||||
[system=$withval],
|
||||
[case "$host_cpu" in
|
||||
i*86)
|
||||
|
@ -66,7 +65,7 @@ AC_SYS_LARGEFILE
|
|||
AC_STRUCT_DIRENT_D_TYPE
|
||||
if test "$sys_name" = sunos; then
|
||||
# Solaris requires -lsocket -lnsl for network functions
|
||||
LIBS="-lsocket -lnsl $LIBS"
|
||||
LDFLAGS="-lsocket -lnsl $LDFLAGS"
|
||||
fi
|
||||
|
||||
|
||||
|
@ -127,8 +126,7 @@ NEED_PROG(jq, jq)
|
|||
AC_SUBST(coreutils, [$(dirname $(type -p cat))])
|
||||
|
||||
|
||||
AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH],
|
||||
[path of the Nix store (defaults to /nix/store)]),
|
||||
AC_ARG_WITH(store-dir, AS_HELP_STRING([--with-store-dir=PATH],[path of the Nix store (defaults to /nix/store)]),
|
||||
storedir=$withval, storedir='/nix/store')
|
||||
AC_SUBST(storedir)
|
||||
|
||||
|
@ -152,13 +150,12 @@ int main() {
|
|||
}]])], GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC=no, GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC=yes)
|
||||
AC_MSG_RESULT($GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC)
|
||||
if test "x$GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC" = xyes; then
|
||||
LDFLAGS="$LDFLAGS -latomic"
|
||||
LDFLAGS="-latomic $LDFLAGS"
|
||||
fi
|
||||
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
AC_ARG_ENABLE(shared, AC_HELP_STRING([--enable-shared],
|
||||
[Build shared libraries for Nix [default=yes]]),
|
||||
AC_ARG_ENABLE(shared, AS_HELP_STRING([--enable-shared],[Build shared libraries for Nix [default=yes]]),
|
||||
shared=$enableval, shared=yes)
|
||||
if test "$shared" = yes; then
|
||||
AC_SUBST(BUILD_SHARED_LIBS, 1, [Whether to build shared libraries.])
|
||||
|
@ -172,11 +169,6 @@ fi
|
|||
PKG_CHECK_MODULES([OPENSSL], [libcrypto], [CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS"])
|
||||
|
||||
|
||||
# Look for libbz2, a required dependency.
|
||||
AC_CHECK_LIB([bz2], [BZ2_bzWriteOpen], [true],
|
||||
[AC_MSG_ERROR([Nix requires libbz2, which is part of bzip2. See https://sourceware.org/bzip2/.])])
|
||||
AC_CHECK_HEADERS([bzlib.h], [true],
|
||||
[AC_MSG_ERROR([Nix requires libbz2, which is part of bzip2. See https://sourceware.org/bzip2/.])])
|
||||
# Checks for libarchive
|
||||
PKG_CHECK_MODULES([LIBARCHIVE], [libarchive >= 3.1.2], [CXXFLAGS="$LIBARCHIVE_CFLAGS $CXXFLAGS"])
|
||||
# Workaround until https://github.com/libarchive/libarchive/issues/1446 is fixed
|
||||
|
@ -205,16 +197,6 @@ PKG_CHECK_MODULES([EDITLINE], [libeditline], [CXXFLAGS="$EDITLINE_CFLAGS $CXXFLA
|
|||
# Look for libsodium, an optional dependency.
|
||||
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
|
||||
|
||||
# Look for liblzma, a required dependency.
|
||||
PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"])
|
||||
AC_CHECK_LIB([lzma], [lzma_stream_encoder_mt],
|
||||
[AC_DEFINE([HAVE_LZMA_MT], [1], [xz multithreaded compression support])])
|
||||
|
||||
# Look for zlib, a required dependency.
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib], [CXXFLAGS="$ZLIB_CFLAGS $CXXFLAGS"])
|
||||
AC_CHECK_HEADER([zlib.h],[:],[AC_MSG_ERROR([could not find the zlib.h header])])
|
||||
LDFLAGS="-lz $LDFLAGS"
|
||||
|
||||
# Look for libbrotli{enc,dec}.
|
||||
PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"])
|
||||
|
||||
|
@ -230,9 +212,8 @@ AC_SUBST(HAVE_LIBCPUID, [$have_libcpuid])
|
|||
# Look for libseccomp, required for Linux sandboxing.
|
||||
if test "$sys_name" = linux; then
|
||||
AC_ARG_ENABLE([seccomp-sandboxing],
|
||||
AC_HELP_STRING([--disable-seccomp-sandboxing],
|
||||
[Don't build support for seccomp sandboxing (only recommended if your arch doesn't support libseccomp yet!)]
|
||||
))
|
||||
AS_HELP_STRING([--disable-seccomp-sandboxing],[Don't build support for seccomp sandboxing (only recommended if your arch doesn't support libseccomp yet!)
|
||||
]))
|
||||
if test "x$enable_seccomp_sandboxing" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp],
|
||||
[CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"])
|
||||
|
@ -250,8 +231,8 @@ AC_SUBST(HAVE_SECCOMP, [$have_seccomp])
|
|||
# Look for aws-cpp-sdk-s3.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([aws/s3/S3Client.h],
|
||||
[AC_DEFINE([ENABLE_S3], [1], [Whether to enable S3 support via aws-sdk-cpp.])
|
||||
enable_s3=1], [enable_s3=])
|
||||
[AC_DEFINE([ENABLE_S3], [1], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=1],
|
||||
[AC_DEFINE([ENABLE_S3], [0], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=])
|
||||
AC_SUBST(ENABLE_S3, [$enable_s3])
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
@ -264,8 +245,7 @@ fi
|
|||
|
||||
|
||||
# Whether to use the Boehm garbage collector.
|
||||
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
||||
[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=yes]]),
|
||||
AC_ARG_ENABLE(gc, AS_HELP_STRING([--enable-gc],[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=yes]]),
|
||||
gc=$enableval, gc=yes)
|
||||
if test "$gc" = yes; then
|
||||
PKG_CHECK_MODULES([BDW_GC], [bdw-gc])
|
||||
|
@ -279,8 +259,7 @@ PKG_CHECK_MODULES([GTEST], [gtest_main])
|
|||
|
||||
|
||||
# documentation generation switch
|
||||
AC_ARG_ENABLE(doc-gen, AC_HELP_STRING([--disable-doc-gen],
|
||||
[disable documentation generation]),
|
||||
AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation generation]),
|
||||
doc_generate=$enableval, doc_generate=yes)
|
||||
AC_SUBST(doc_generate)
|
||||
|
||||
|
@ -300,19 +279,7 @@ if test "$(uname)" = "Darwin"; then
|
|||
fi
|
||||
|
||||
|
||||
# Do we have GNU tar?
|
||||
AC_MSG_CHECKING([if you have a recent GNU tar])
|
||||
if $tar --version 2> /dev/null | grep -q GNU && tar cvf /dev/null --warning=no-timestamp ./config.log > /dev/null; then
|
||||
AC_MSG_RESULT(yes)
|
||||
tarFlags="--warning=no-timestamp"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
AC_SUBST(tarFlags)
|
||||
|
||||
|
||||
AC_ARG_WITH(sandbox-shell, AC_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)
|
||||
AC_SUBST(sandbox_shell)
|
||||
|
||||
|
@ -327,6 +294,6 @@ done
|
|||
|
||||
rm -f Makefile.config
|
||||
|
||||
AC_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_FILES([])
|
||||
AC_OUTPUT
|
||||
|
|
|
@ -25,19 +25,19 @@ nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -
|
|||
$(d)/%.1: $(d)/src/command-ref/%.md
|
||||
@printf "Title: %s\n\n" "$$(basename $@ .1)" > $^.tmp
|
||||
@cat $^ >> $^.tmp
|
||||
$(trace-gen) lowdown -sT man $^.tmp -o $@
|
||||
$(trace-gen) lowdown -sT man -M section=1 $^.tmp -o $@
|
||||
@rm $^.tmp
|
||||
|
||||
$(d)/%.8: $(d)/src/command-ref/%.md
|
||||
@printf "Title: %s\n\n" "$$(basename $@ .8)" > $^.tmp
|
||||
@cat $^ >> $^.tmp
|
||||
$(trace-gen) lowdown -sT man $^.tmp -o $@
|
||||
$(trace-gen) lowdown -sT man -M section=8 $^.tmp -o $@
|
||||
@rm $^.tmp
|
||||
|
||||
$(d)/nix.conf.5: $(d)/src/command-ref/conf-file.md
|
||||
@printf "Title: %s\n\n" "$$(basename $@ .5)" > $^.tmp
|
||||
@cat $^ >> $^.tmp
|
||||
$(trace-gen) lowdown -sT man $^.tmp -o $@
|
||||
$(trace-gen) lowdown -sT man -M section=5 $^.tmp -o $@
|
||||
@rm $^.tmp
|
||||
|
||||
$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
|
||||
|
@ -80,7 +80,7 @@ install: $(d)/src/command-ref/new-cli
|
|||
if [[ $$name = SUMMARY ]]; then continue; fi; \
|
||||
printf "Title: %s\n\n" "$$name" > $$i.tmp; \
|
||||
cat $$i >> $$i.tmp; \
|
||||
lowdown -sT man $$i.tmp -o $(mandir)/man1/$$name.1; \
|
||||
lowdown -sT man -M section=1 $$i.tmp -o $(mandir)/man1/$$name.1; \
|
||||
done
|
||||
|
||||
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(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
|
||||
|
|
|
@ -4,13 +4,13 @@ Nix has two relevant settings with regards to how your CPU cores will
|
|||
be utilized: `cores` and `max-jobs`. This chapter will talk about what
|
||||
they are, how they interact, and their configuration trade-offs.
|
||||
|
||||
- `max-jobs`
|
||||
- `max-jobs`\
|
||||
Dictates how many separate derivations will be built at the same
|
||||
time. If you set this to zero, the local machine will do no
|
||||
builds. Nix will still substitute from binary caches, and build
|
||||
remotely if remote builders are configured.
|
||||
|
||||
- `cores`
|
||||
- `cores`\
|
||||
Suggests how many cores each derivation should use. Similar to
|
||||
`make -j`.
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
Most Nix commands interpret the following environment variables:
|
||||
|
||||
- `IN_NIX_SHELL`
|
||||
- `IN_NIX_SHELL`\
|
||||
Indicator that tells if the current environment was set up by
|
||||
`nix-shell`. Since Nix 2.0 the values are `"pure"` and `"impure"`
|
||||
|
||||
- `NIX_PATH`
|
||||
- `NIX_PATH`\
|
||||
A colon-separated list of directories used to look up Nix
|
||||
expressions enclosed in angle brackets (i.e., `<path>`). For
|
||||
instance, the value
|
||||
|
@ -28,19 +28,23 @@ Most Nix commands interpret the following environment variables:
|
|||
consist of a single top-level directory. For example, setting
|
||||
`NIX_PATH` to
|
||||
|
||||
nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-15.09.tar.gz
|
||||
nixpkgs=https://github.com/NixOS/nixpkgs/archive/master.tar.gz
|
||||
|
||||
tells Nix to download the latest revision in the Nixpkgs/NixOS 15.09
|
||||
channel.
|
||||
tells Nix to download and use the current contents of the
|
||||
`master` branch in the `nixpkgs` repository.
|
||||
|
||||
A following shorthand can be used to refer to the official channels:
|
||||
The URLs of the tarballs from the official nixos.org channels (see
|
||||
[the manual for `nix-channel`](nix-channel.md)) can be abbreviated
|
||||
as `channel:<channel-name>`. For instance, the following two
|
||||
values of `NIX_PATH` are equivalent:
|
||||
|
||||
nixpkgs=channel:nixos-15.09
|
||||
nixpkgs=channel:nixos-21.05
|
||||
nixpkgs=https://nixos.org/channels/nixos-21.05/nixexprs.tar.xz
|
||||
|
||||
The search path can be extended using the `-I` option, which takes
|
||||
precedence over `NIX_PATH`.
|
||||
The Nix search path can also be extended using the `-I` option to
|
||||
many Nix commands, which takes precedence over `NIX_PATH`.
|
||||
|
||||
- `NIX_IGNORE_SYMLINK_STORE`
|
||||
- `NIX_IGNORE_SYMLINK_STORE`\
|
||||
Normally, the Nix store directory (typically `/nix/store`) is not
|
||||
allowed to contain any symlink components. This is to prevent
|
||||
“impure” builds. Builders sometimes “canonicalise” paths by
|
||||
|
@ -62,41 +66,41 @@ Most Nix commands interpret the following environment variables:
|
|||
|
||||
Consult the mount 8 manual page for details.
|
||||
|
||||
- `NIX_STORE_DIR`
|
||||
- `NIX_STORE_DIR`\
|
||||
Overrides the location of the Nix store (default `prefix/store`).
|
||||
|
||||
- `NIX_DATA_DIR`
|
||||
- `NIX_DATA_DIR`\
|
||||
Overrides the location of the Nix static data directory (default
|
||||
`prefix/share`).
|
||||
|
||||
- `NIX_LOG_DIR`
|
||||
- `NIX_LOG_DIR`\
|
||||
Overrides the location of the Nix log directory (default
|
||||
`prefix/var/log/nix`).
|
||||
|
||||
- `NIX_STATE_DIR`
|
||||
- `NIX_STATE_DIR`\
|
||||
Overrides the location of the Nix state directory (default
|
||||
`prefix/var/nix`).
|
||||
|
||||
- `NIX_CONF_DIR`
|
||||
- `NIX_CONF_DIR`\
|
||||
Overrides the location of the system Nix configuration directory
|
||||
(default `prefix/etc/nix`).
|
||||
|
||||
- `NIX_CONFIG`
|
||||
- `NIX_CONFIG`\
|
||||
Applies settings from Nix configuration from the environment.
|
||||
The content is treated as if it was read from a Nix configuration file.
|
||||
Settings are separated by the newline character.
|
||||
|
||||
- `NIX_USER_CONF_FILES`
|
||||
- `NIX_USER_CONF_FILES`\
|
||||
Overrides the location of the user Nix configuration files to load
|
||||
from (defaults to the XDG spec locations). The variable is treated
|
||||
as a list separated by the `:` token.
|
||||
|
||||
- `TMPDIR`
|
||||
- `TMPDIR`\
|
||||
Use the specified directory to store temporary files. In particular,
|
||||
this includes temporary build directories; these can take up
|
||||
substantial amounts of disk space. The default is `/tmp`.
|
||||
|
||||
- `NIX_REMOTE`
|
||||
- `NIX_REMOTE`\
|
||||
This variable should be set to `daemon` if you want to use the Nix
|
||||
daemon to execute Nix operations. This is necessary in [multi-user
|
||||
Nix installations](../installation/multi-user.md). If the Nix
|
||||
|
@ -104,16 +108,16 @@ Most Nix commands interpret the following environment variables:
|
|||
should be set to `unix://path/to/socket`. Otherwise, it should be
|
||||
left unset.
|
||||
|
||||
- `NIX_SHOW_STATS`
|
||||
- `NIX_SHOW_STATS`\
|
||||
If set to `1`, Nix will print some evaluation statistics, such as
|
||||
the number of values allocated.
|
||||
|
||||
- `NIX_COUNT_CALLS`
|
||||
- `NIX_COUNT_CALLS`\
|
||||
If set to `1`, Nix will print how often functions were called during
|
||||
Nix expression evaluation. This is useful for profiling your Nix
|
||||
expressions.
|
||||
|
||||
- `GC_INITIAL_HEAP_SIZE`
|
||||
- `GC_INITIAL_HEAP_SIZE`\
|
||||
If Nix has been configured to use the Boehm garbage collector, this
|
||||
variable sets the initial size of the heap in bytes. It defaults to
|
||||
384 MiB. Setting it to a low value reduces memory consumption, but
|
||||
|
|
|
@ -47,16 +47,16 @@ All options not listed here are passed to `nix-store
|
|||
--realise`, except for `--arg` and `--attr` / `-A` which are passed to
|
||||
`nix-instantiate`.
|
||||
|
||||
- `--no-out-link`
|
||||
- `--no-out-link`\
|
||||
Do not create a symlink to the output path. Note that as a result
|
||||
the output does not become a root of the garbage collector, and so
|
||||
might be deleted by `nix-store
|
||||
--gc`.
|
||||
|
||||
- `--dry-run`
|
||||
- `--dry-run`\
|
||||
Show what store paths would be built or downloaded.
|
||||
|
||||
- `--out-link` / `-o` *outlink*
|
||||
- `--out-link` / `-o` *outlink*\
|
||||
Change the name of the symlink to the output path created from
|
||||
`result` to *outlink*.
|
||||
|
||||
|
|
|
@ -17,26 +17,26 @@ To see the list of official NixOS channels, visit
|
|||
|
||||
This command has the following operations:
|
||||
|
||||
- `--add` *url* \[*name*\]
|
||||
- `--add` *url* \[*name*\]\
|
||||
Adds a channel named *name* with URL *url* to the list of subscribed
|
||||
channels. If *name* is omitted, it defaults to the last component of
|
||||
*url*, with the suffixes `-stable` or `-unstable` removed.
|
||||
|
||||
- `--remove` *name*
|
||||
- `--remove` *name*\
|
||||
Removes the channel named *name* from the list of subscribed
|
||||
channels.
|
||||
|
||||
- `--list`
|
||||
- `--list`\
|
||||
Prints the names and URLs of all subscribed channels on standard
|
||||
output.
|
||||
|
||||
- `--update` \[*names*…\]
|
||||
- `--update` \[*names*…\]\
|
||||
Downloads the Nix expressions of all subscribed channels (or only
|
||||
those included in *names* if specified) and makes them the default
|
||||
for `nix-env` operations (by symlinking them from the directory
|
||||
`~/.nix-defexpr`).
|
||||
|
||||
- `--rollback` \[*generation*\]
|
||||
- `--rollback` \[*generation*\]\
|
||||
Reverts the previous call to `nix-channel
|
||||
--update`. Optionally, you can specify a specific channel generation
|
||||
number to restore.
|
||||
|
@ -70,14 +70,14 @@ $ nix-instantiate --eval -E '(import <nixpkgs> {}).lib.version'
|
|||
|
||||
# Files
|
||||
|
||||
- `/nix/var/nix/profiles/per-user/username/channels`
|
||||
- `/nix/var/nix/profiles/per-user/username/channels`\
|
||||
`nix-channel` uses a `nix-env` profile to keep track of previous
|
||||
versions of the subscribed channels. Every time you run `nix-channel
|
||||
--update`, a new channel generation (that is, a symlink to the
|
||||
channel Nix expressions in the Nix store) is created. This enables
|
||||
`nix-channel --rollback` to revert to previous versions.
|
||||
|
||||
- `~/.nix-defexpr/channels`
|
||||
- `~/.nix-defexpr/channels`\
|
||||
This is a symlink to
|
||||
`/nix/var/nix/profiles/per-user/username/channels`. It ensures that
|
||||
`nix-env` can find your channels. In a multi-user installation, you
|
||||
|
@ -89,7 +89,7 @@ $ nix-instantiate --eval -E '(import <nixpkgs> {}).lib.version'
|
|||
A channel URL should point to a directory containing the following
|
||||
files:
|
||||
|
||||
- `nixexprs.tar.xz`
|
||||
- `nixexprs.tar.xz`\
|
||||
A tarball containing Nix expressions and files referenced by them
|
||||
(such as build scripts and patches). At the top level, the tarball
|
||||
should contain a single directory. That directory must contain a
|
||||
|
|
|
@ -35,21 +35,21 @@ and second to send the dump of those paths. If this bothers you, use
|
|||
|
||||
# Options
|
||||
|
||||
- `--to`
|
||||
- `--to`\
|
||||
Copy the closure of _paths_ from the local Nix store to the Nix
|
||||
store on _machine_. This is the default.
|
||||
|
||||
- `--from`
|
||||
- `--from`\
|
||||
Copy the closure of _paths_ from the Nix store on _machine_ to the
|
||||
local Nix store.
|
||||
|
||||
- `--gzip`
|
||||
- `--gzip`\
|
||||
Enable compression of the SSH connection.
|
||||
|
||||
- `--include-outputs`
|
||||
- `--include-outputs`\
|
||||
Also copy the outputs of store derivations included in the closure.
|
||||
|
||||
- `--use-substitutes` / `-s`
|
||||
- `--use-substitutes` / `-s`\
|
||||
Attempt to download missing paths on the target machine using Nix’s
|
||||
substitute mechanism. Any paths that cannot be substituted on the
|
||||
target are still copied normally from the source. This is useful,
|
||||
|
@ -58,12 +58,12 @@ and second to send the dump of those paths. If this bothers you, use
|
|||
`nixos.org` (the default binary cache server) is
|
||||
fast.
|
||||
|
||||
- `-v`
|
||||
- `-v`\
|
||||
Show verbose output.
|
||||
|
||||
# Environment variables
|
||||
|
||||
- `NIX_SSHOPTS`
|
||||
- `NIX_SSHOPTS`\
|
||||
Additional options to be passed to `ssh` on the command
|
||||
line.
|
||||
|
||||
|
|
|
@ -36,27 +36,27 @@ case-sensitive. The regular expression can optionally be followed by a
|
|||
dash and a version number; if omitted, any version of the package will
|
||||
match. Here are some examples:
|
||||
|
||||
- `firefox`
|
||||
- `firefox`\
|
||||
Matches the package name `firefox` and any version.
|
||||
|
||||
- `firefox-32.0`
|
||||
- `firefox-32.0`\
|
||||
Matches the package name `firefox` and version `32.0`.
|
||||
|
||||
- `gtk\\+`
|
||||
- `gtk\\+`\
|
||||
Matches the package name `gtk+`. The `+` character must be escaped
|
||||
using a backslash to prevent it from being interpreted as a
|
||||
quantifier, and the backslash must be escaped in turn with another
|
||||
backslash to ensure that the shell passes it on.
|
||||
|
||||
- `.\*`
|
||||
- `.\*`\
|
||||
Matches any package name. This is the default for most commands.
|
||||
|
||||
- `'.*zip.*'`
|
||||
- `'.*zip.*'`\
|
||||
Matches any package name containing the string `zip`. Note the dots:
|
||||
`'*zip*'` does not work, because in a regular expression, the
|
||||
character `*` is interpreted as a quantifier.
|
||||
|
||||
- `'.*(firefox|chromium).*'`
|
||||
- `'.*(firefox|chromium).*'`\
|
||||
Matches any package name containing the strings `firefox` or
|
||||
`chromium`.
|
||||
|
||||
|
@ -66,7 +66,7 @@ This section lists the options that are common to all operations. These
|
|||
options are allowed for every subcommand, though they may not always
|
||||
have an effect.
|
||||
|
||||
- `--file` / `-f` *path*
|
||||
- `--file` / `-f` *path*\
|
||||
Specifies the Nix expression (designated below as the *active Nix
|
||||
expression*) used by the `--install`, `--upgrade`, and `--query
|
||||
--available` operations to obtain derivations. The default is
|
||||
|
@ -77,13 +77,13 @@ have an effect.
|
|||
unpacked to a temporary location. The tarball must include a single
|
||||
top-level directory containing at least a file named `default.nix`.
|
||||
|
||||
- `--profile` / `-p` *path*
|
||||
- `--profile` / `-p` *path*\
|
||||
Specifies the profile to be used by those operations that operate on
|
||||
a profile (designated below as the *active profile*). A profile is a
|
||||
sequence of user environments called *generations*, one of which is
|
||||
the *current generation*.
|
||||
|
||||
- `--dry-run`
|
||||
- `--dry-run`\
|
||||
For the `--install`, `--upgrade`, `--uninstall`,
|
||||
`--switch-generation`, `--delete-generations` and `--rollback`
|
||||
operations, this flag will cause `nix-env` to print what *would* be
|
||||
|
@ -93,7 +93,7 @@ have an effect.
|
|||
[substituted](../glossary.md) (i.e., downloaded) and which paths
|
||||
will be built from source (because no substitute is available).
|
||||
|
||||
- `--system-filter` *system*
|
||||
- `--system-filter` *system*\
|
||||
By default, operations such as `--query
|
||||
--available` show derivations matching any platform. This option
|
||||
allows you to use derivations for the specified platform *system*.
|
||||
|
@ -102,7 +102,7 @@ have an effect.
|
|||
|
||||
# Files
|
||||
|
||||
- `~/.nix-defexpr`
|
||||
- `~/.nix-defexpr`\
|
||||
The source for the default Nix expressions used by the
|
||||
`--install`, `--upgrade`, and `--query --available` operations to
|
||||
obtain derivations. The `--file` option may be used to override
|
||||
|
@ -140,7 +140,7 @@ have an effect.
|
|||
The command `nix-channel` places symlinks to the downloaded Nix
|
||||
expressions from each subscribed channel in this directory.
|
||||
|
||||
- `~/.nix-profile`
|
||||
- `~/.nix-profile`\
|
||||
A symbolic link to the user's current profile. By default, this
|
||||
symlink points to `prefix/var/nix/profiles/default`. The `PATH`
|
||||
environment variable should include `~/.nix-profile/bin` for the
|
||||
|
@ -217,13 +217,13 @@ a number of possible ways:
|
|||
|
||||
## Flags
|
||||
|
||||
- `--prebuilt-only` / `-b`
|
||||
- `--prebuilt-only` / `-b`\
|
||||
Use only derivations for which a substitute is registered, i.e.,
|
||||
there is a pre-built binary available that can be downloaded in lieu
|
||||
of building the derivation. Thus, no packages will be built from
|
||||
source.
|
||||
|
||||
- `--preserve-installed`; `-P`
|
||||
- `--preserve-installed`; `-P`\
|
||||
Do not remove derivations with a name matching one of the
|
||||
derivations being installed. Usually, trying to have two versions of
|
||||
the same package installed in the same generation of a profile will
|
||||
|
@ -231,7 +231,7 @@ a number of possible ways:
|
|||
clashes between the two versions. However, this is not the case for
|
||||
all packages.
|
||||
|
||||
- `--remove-all`; `-r`
|
||||
- `--remove-all`; `-r`\
|
||||
Remove all previously installed packages first. This is equivalent
|
||||
to running `nix-env -e '.*'` first, except that everything happens
|
||||
in a single transaction.
|
||||
|
@ -346,24 +346,24 @@ version is installed.
|
|||
|
||||
## Flags
|
||||
|
||||
- `--lt`
|
||||
- `--lt`\
|
||||
Only upgrade a derivation to newer versions. This is the default.
|
||||
|
||||
- `--leq`
|
||||
- `--leq`\
|
||||
In addition to upgrading to newer versions, also “upgrade” to
|
||||
derivations that have the same version. Version are not a unique
|
||||
identification of a derivation, so there may be many derivations
|
||||
that have the same version. This flag may be useful to force
|
||||
“synchronisation” between the installed and available derivations.
|
||||
|
||||
- `--eq`
|
||||
- `--eq`\
|
||||
*Only* “upgrade” to derivations that have the same version. This may
|
||||
not seem very useful, but it actually is, e.g., when there is a new
|
||||
release of Nixpkgs and you want to replace installed applications
|
||||
with the same versions built against newer dependencies (to reduce
|
||||
the number of dependencies floating around on your system).
|
||||
|
||||
- `--always`
|
||||
- `--always`\
|
||||
In addition to upgrading to newer versions, also “upgrade” to
|
||||
derivations that have the same or a lower version. I.e., derivations
|
||||
may actually be downgraded depending on what is available in the
|
||||
|
@ -578,11 +578,11 @@ The derivations are sorted by their `name` attributes.
|
|||
The following flags specify the set of things on which the query
|
||||
operates.
|
||||
|
||||
- `--installed`
|
||||
- `--installed`\
|
||||
The query operates on the store paths that are installed in the
|
||||
current generation of the active profile. This is the default.
|
||||
|
||||
- `--available`; `-a`
|
||||
- `--available`; `-a`\
|
||||
The query operates on the derivations that are available in the
|
||||
active Nix expression.
|
||||
|
||||
|
@ -593,24 +593,24 @@ selected derivations. Multiple flags may be specified, in which case the
|
|||
information is shown in the order given here. Note that the name of the
|
||||
derivation is shown unless `--no-name` is specified.
|
||||
|
||||
- `--xml`
|
||||
- `--xml`\
|
||||
Print the result in an XML representation suitable for automatic
|
||||
processing by other tools. The root element is called `items`, which
|
||||
contains a `item` element for each available or installed
|
||||
derivation. The fields discussed below are all stored in attributes
|
||||
of the `item` elements.
|
||||
|
||||
- `--json`
|
||||
- `--json`\
|
||||
Print the result in a JSON representation suitable for automatic
|
||||
processing by other tools.
|
||||
|
||||
- `--prebuilt-only` / `-b`
|
||||
- `--prebuilt-only` / `-b`\
|
||||
Show only derivations for which a substitute is registered, i.e.,
|
||||
there is a pre-built binary available that can be downloaded in lieu
|
||||
of building the derivation. Thus, this shows all packages that
|
||||
probably can be installed quickly.
|
||||
|
||||
- `--status`; `-s`
|
||||
- `--status`; `-s`\
|
||||
Print the *status* of the derivation. The status consists of three
|
||||
characters. The first is `I` or `-`, indicating whether the
|
||||
derivation is currently installed in the current generation of the
|
||||
|
@ -621,49 +621,49 @@ derivation is shown unless `--no-name` is specified.
|
|||
derivation to be built. The third is `S` or `-`, indicating whether
|
||||
a substitute is available for the derivation.
|
||||
|
||||
- `--attr-path`; `-P`
|
||||
- `--attr-path`; `-P`\
|
||||
Print the *attribute path* of the derivation, which can be used to
|
||||
unambiguously select it using the `--attr` option available in
|
||||
commands that install derivations like `nix-env --install`. This
|
||||
option only works together with `--available`
|
||||
|
||||
- `--no-name`
|
||||
- `--no-name`\
|
||||
Suppress printing of the `name` attribute of each derivation.
|
||||
|
||||
- `--compare-versions` / `-c`
|
||||
- `--compare-versions` / `-c`\
|
||||
Compare installed versions to available versions, or vice versa (if
|
||||
`--available` is given). This is useful for quickly seeing whether
|
||||
upgrades for installed packages are available in a Nix expression. A
|
||||
column is added with the following meaning:
|
||||
|
||||
- `<` *version*
|
||||
- `<` *version*\
|
||||
A newer version of the package is available or installed.
|
||||
|
||||
- `=` *version*
|
||||
- `=` *version*\
|
||||
At most the same version of the package is available or
|
||||
installed.
|
||||
|
||||
- `>` *version*
|
||||
- `>` *version*\
|
||||
Only older versions of the package are available or installed.
|
||||
|
||||
- `- ?`
|
||||
- `- ?`\
|
||||
No version of the package is available or installed.
|
||||
|
||||
- `--system`
|
||||
- `--system`\
|
||||
Print the `system` attribute of the derivation.
|
||||
|
||||
- `--drv-path`
|
||||
- `--drv-path`\
|
||||
Print the path of the store derivation.
|
||||
|
||||
- `--out-path`
|
||||
- `--out-path`\
|
||||
Print the output path of the derivation.
|
||||
|
||||
- `--description`
|
||||
- `--description`\
|
||||
Print a short (one-line) description of the derivation, if
|
||||
available. The description is taken from the `meta.description`
|
||||
attribute of the derivation.
|
||||
|
||||
- `--meta`
|
||||
- `--meta`\
|
||||
Print all of the meta-attributes of the derivation. This option is
|
||||
only available with `--xml` or `--json`.
|
||||
|
||||
|
@ -874,7 +874,7 @@ error: no generation older than the current (91) exists
|
|||
|
||||
# Environment variables
|
||||
|
||||
- `NIX_PROFILE`
|
||||
- `NIX_PROFILE`\
|
||||
Location of the Nix profile. Defaults to the target of the symlink
|
||||
`~/.nix-profile`, if it exists, or `/nix/var/nix/profiles/default`
|
||||
otherwise.
|
||||
|
|
|
@ -29,29 +29,29 @@ md5sum`.
|
|||
|
||||
# Options
|
||||
|
||||
- `--flat`
|
||||
- `--flat`\
|
||||
Print the cryptographic hash of the contents of each regular file
|
||||
*path*. That is, do not compute the hash over the dump of *path*.
|
||||
The result is identical to that produced by the GNU commands
|
||||
`md5sum` and `sha1sum`.
|
||||
|
||||
- `--base32`
|
||||
- `--base32`\
|
||||
Print the hash in a base-32 representation rather than hexadecimal.
|
||||
This base-32 representation is more compact and can be used in Nix
|
||||
expressions (such as in calls to `fetchurl`).
|
||||
|
||||
- `--truncate`
|
||||
- `--truncate`\
|
||||
Truncate hashes longer than 160 bits (such as SHA-256) to 160 bits.
|
||||
|
||||
- `--type` *hashAlgo*
|
||||
- `--type` *hashAlgo*\
|
||||
Use the specified cryptographic hash algorithm, which can be one of
|
||||
`md5`, `sha1`, `sha256`, and `sha512`.
|
||||
|
||||
- `--to-base16`
|
||||
- `--to-base16`\
|
||||
Don’t hash anything, but convert the base-32 hash representation
|
||||
*hash* to hexadecimal.
|
||||
|
||||
- `--to-base32`
|
||||
- `--to-base32`\
|
||||
Don’t hash anything, but convert the hexadecimal hash representation
|
||||
*hash* to base-32.
|
||||
|
||||
|
|
|
@ -29,26 +29,26 @@ standard input.
|
|||
|
||||
# Options
|
||||
|
||||
- `--add-root` *path*
|
||||
- `--add-root` *path*\
|
||||
See the [corresponding option](nix-store.md) in `nix-store`.
|
||||
|
||||
- `--parse`
|
||||
- `--parse`\
|
||||
Just parse the input files, and print their abstract syntax trees on
|
||||
standard output in ATerm format.
|
||||
|
||||
- `--eval`
|
||||
- `--eval`\
|
||||
Just parse and evaluate the input files, and print the resulting
|
||||
values on standard output. No instantiation of store derivations
|
||||
takes place.
|
||||
|
||||
- `--find-file`
|
||||
- `--find-file`\
|
||||
Look up the given files in Nix’s search path (as specified by the
|
||||
`NIX_PATH` environment variable). If found, print the corresponding
|
||||
absolute paths on standard output. For instance, if `NIX_PATH` is
|
||||
`nixpkgs=/home/alice/nixpkgs`, then `nix-instantiate --find-file
|
||||
nixpkgs/default.nix` will print `/home/alice/nixpkgs/default.nix`.
|
||||
|
||||
- `--strict`
|
||||
- `--strict`\
|
||||
When used with `--eval`, recursively evaluate list elements and
|
||||
attributes. Normally, such sub-expressions are left unevaluated
|
||||
(since the Nix expression language is lazy).
|
||||
|
@ -58,17 +58,17 @@ standard input.
|
|||
> This option can cause non-termination, because lazy data
|
||||
> structures can be infinitely large.
|
||||
|
||||
- `--json`
|
||||
- `--json`\
|
||||
When used with `--eval`, print the resulting value as an JSON
|
||||
representation of the abstract syntax tree rather than as an ATerm.
|
||||
|
||||
- `--xml`
|
||||
- `--xml`\
|
||||
When used with `--eval`, print the resulting value as an XML
|
||||
representation of the abstract syntax tree rather than as an ATerm.
|
||||
The schema is the same as that used by the [`toXML`
|
||||
built-in](../expressions/builtins.md).
|
||||
|
||||
- `--read-write-mode`
|
||||
- `--read-write-mode`\
|
||||
When used with `--eval`, perform evaluation in read/write mode so
|
||||
nix language features that require it will still work (at the cost
|
||||
of needing to do instantiation of every evaluated derivation). If
|
||||
|
|
|
@ -37,22 +37,22 @@ Nix store is also printed.
|
|||
|
||||
# Options
|
||||
|
||||
- `--type` *hashAlgo*
|
||||
- `--type` *hashAlgo*\
|
||||
Use the specified cryptographic hash algorithm, which can be one of
|
||||
`md5`, `sha1`, `sha256`, and `sha512`.
|
||||
|
||||
- `--print-path`
|
||||
- `--print-path`\
|
||||
Print the store path of the downloaded file on standard output.
|
||||
|
||||
- `--unpack`
|
||||
- `--unpack`\
|
||||
Unpack the archive (which must be a tarball or zip file) and add the
|
||||
result to the Nix store. The resulting hash can be used with
|
||||
functions such as Nixpkgs’s `fetchzip` or `fetchFromGitHub`.
|
||||
|
||||
- `--executable`
|
||||
- `--executable`\
|
||||
Set the executable bit on the downloaded file.
|
||||
|
||||
- `--name` *name*
|
||||
- `--name` *name*\
|
||||
Override the name of the file in the Nix store. By default, this is
|
||||
`hash-basename`, where *basename* is the last component of *url*.
|
||||
Overriding the name is necessary when *basename* contains characters
|
||||
|
|
|
@ -54,7 +54,7 @@ All options not listed here are passed to `nix-store
|
|||
--realise`, except for `--arg` and `--attr` / `-A` which are passed to
|
||||
`nix-instantiate`.
|
||||
|
||||
- `--command` *cmd*
|
||||
- `--command` *cmd*\
|
||||
In the environment of the derivation, run the shell command *cmd*.
|
||||
This command is executed in an interactive shell. (Use `--run` to
|
||||
use a non-interactive shell instead.) However, a call to `exit` is
|
||||
|
@ -64,36 +64,34 @@ All options not listed here are passed to `nix-store
|
|||
drop you into the interactive shell. This can be useful for doing
|
||||
any additional initialisation.
|
||||
|
||||
- `--run` *cmd*
|
||||
- `--run` *cmd*\
|
||||
Like `--command`, but executes the command in a non-interactive
|
||||
shell. This means (among other things) that if you hit Ctrl-C while
|
||||
the command is running, the shell exits.
|
||||
|
||||
- `--exclude` *regexp*
|
||||
- `--exclude` *regexp*\
|
||||
Do not build any dependencies whose store path matches the regular
|
||||
expression *regexp*. This option may be specified multiple times.
|
||||
|
||||
- `--pure`
|
||||
- `--pure`\
|
||||
If this flag is specified, the environment is almost entirely
|
||||
cleared before the interactive shell is started, so you get an
|
||||
environment that more closely corresponds to the “real” Nix build. A
|
||||
few variables, in particular `HOME`, `USER` and `DISPLAY`, are
|
||||
retained. Note that (depending on your Bash
|
||||
installation) `/etc/bashrc` is still sourced, so any variables set
|
||||
there will affect the interactive shell.
|
||||
retained.
|
||||
|
||||
- `--packages` / `-p` *packages*…
|
||||
- `--packages` / `-p` *packages*…\
|
||||
Set up an environment in which the specified packages are present.
|
||||
The command line arguments are interpreted as attribute names inside
|
||||
the Nix Packages collection. Thus, `nix-shell -p libjpeg openjdk`
|
||||
will start a shell in which the packages denoted by the attribute
|
||||
names `libjpeg` and `openjdk` are present.
|
||||
|
||||
- `-i` *interpreter*
|
||||
- `-i` *interpreter*\
|
||||
The chained script interpreter to be invoked by `nix-shell`. Only
|
||||
applicable in `#!`-scripts (described below).
|
||||
|
||||
- `--keep` *name*
|
||||
- `--keep` *name*\
|
||||
When a `--pure` shell is started, keep the listed environment
|
||||
variables.
|
||||
|
||||
|
@ -101,7 +99,7 @@ The following common options are supported:
|
|||
|
||||
# Environment variables
|
||||
|
||||
- `NIX_BUILD_SHELL`
|
||||
- `NIX_BUILD_SHELL`\
|
||||
Shell used to start the interactive environment. Defaults to the
|
||||
`bash` found in `PATH`.
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ This section lists the options that are common to all operations. These
|
|||
options are allowed for every subcommand, though they may not always
|
||||
have an effect.
|
||||
|
||||
- `--add-root` *path*
|
||||
- `--add-root` *path*\
|
||||
Causes the result of a realisation (`--realise` and
|
||||
`--force-realise`) to be registered as a root of the garbage
|
||||
collector. *path* will be created as a symlink to the resulting
|
||||
|
@ -79,22 +79,22 @@ paths. Realisation is a somewhat overloaded term:
|
|||
system). If the path is already valid, we are done immediately.
|
||||
Otherwise, the path and any missing paths in its closure may be
|
||||
produced through substitutes. If there are no (successful)
|
||||
subsitutes, realisation fails.
|
||||
substitutes, realisation fails.
|
||||
|
||||
The output path of each derivation is printed on standard output. (For
|
||||
non-derivations argument, the argument itself is printed.)
|
||||
|
||||
The following flags are available:
|
||||
|
||||
- `--dry-run`
|
||||
- `--dry-run`\
|
||||
Print on standard error a description of what packages would be
|
||||
built or downloaded, without actually performing the operation.
|
||||
|
||||
- `--ignore-unknown`
|
||||
- `--ignore-unknown`\
|
||||
If a non-derivation path does not have a substitute, then silently
|
||||
ignore it.
|
||||
|
||||
- `--check`
|
||||
- `--check`\
|
||||
This option allows you to check whether a derivation is
|
||||
deterministic. It rebuilds the specified derivation and checks
|
||||
whether the result is bitwise-identical with the existing outputs,
|
||||
|
@ -110,20 +110,20 @@ The following flags are available:
|
|||
|
||||
Special exit codes:
|
||||
|
||||
- `100`
|
||||
- `100`\
|
||||
Generic build failure, the builder process returned with a non-zero
|
||||
exit code.
|
||||
|
||||
- `101`
|
||||
- `101`\
|
||||
Build timeout, the build was aborted because it did not complete
|
||||
within the specified `timeout`.
|
||||
|
||||
- `102`
|
||||
- `102`\
|
||||
Hash mismatch, the build output was rejected because it does not
|
||||
match the [`outputHash` attribute of the
|
||||
derivation](../expressions/advanced-attributes.md).
|
||||
|
||||
- `104`
|
||||
- `104`\
|
||||
Not deterministic, the build succeeded in check mode but the
|
||||
resulting output is not binary reproducable.
|
||||
|
||||
|
@ -170,7 +170,7 @@ access to a restricted ssh user.
|
|||
|
||||
The following flags are available:
|
||||
|
||||
- `--write`
|
||||
- `--write`\
|
||||
Allow the connected client to request the realization of
|
||||
derivations. In effect, this can be used to make the host act as a
|
||||
remote builder.
|
||||
|
@ -200,18 +200,18 @@ reachable via file system references from a set of “roots”, are deleted.
|
|||
|
||||
The following suboperations may be specified:
|
||||
|
||||
- `--print-roots`
|
||||
- `--print-roots`\
|
||||
This operation prints on standard output the set of roots used by
|
||||
the garbage collector.
|
||||
|
||||
- `--print-live`
|
||||
- `--print-live`\
|
||||
This operation prints on standard output the set of “live” store
|
||||
paths, which are all the store paths reachable from the roots. Live
|
||||
paths should never be deleted, since that would break consistency —
|
||||
it would become possible that applications are installed that
|
||||
reference things that are no longer present in the store.
|
||||
|
||||
- `--print-dead`
|
||||
- `--print-dead`\
|
||||
This operation prints out on standard output the set of “dead” store
|
||||
paths, which is just the opposite of the set of live paths: any path
|
||||
in the store that is not live (with respect to the roots) is dead.
|
||||
|
@ -219,7 +219,7 @@ The following suboperations may be specified:
|
|||
By default, all unreachable paths are deleted. The following options
|
||||
control what gets deleted and in what order:
|
||||
|
||||
- `--max-freed` *bytes*
|
||||
- `--max-freed` *bytes*\
|
||||
Keep deleting paths until at least *bytes* bytes have been deleted,
|
||||
then stop. The argument *bytes* can be followed by the
|
||||
multiplicative suffix `K`, `M`, `G` or `T`, denoting KiB, MiB, GiB
|
||||
|
@ -300,22 +300,22 @@ symlink.
|
|||
|
||||
## Common query options
|
||||
|
||||
- `--use-output`; `-u`
|
||||
- `--use-output`; `-u`\
|
||||
For each argument to the query that is a store derivation, apply the
|
||||
query to the output path of the derivation instead.
|
||||
|
||||
- `--force-realise`; `-f`
|
||||
- `--force-realise`; `-f`\
|
||||
Realise each argument to the query first (see [`nix-store
|
||||
--realise`](#operation---realise)).
|
||||
|
||||
## Queries
|
||||
|
||||
- `--outputs`
|
||||
- `--outputs`\
|
||||
Prints out the [output paths](../glossary.md) of the store
|
||||
derivations *paths*. These are the paths that will be produced when
|
||||
the derivation is built.
|
||||
|
||||
- `--requisites`; `-R`
|
||||
- `--requisites`; `-R`\
|
||||
Prints out the [closure](../glossary.md) of the store path *paths*.
|
||||
|
||||
This query has one option:
|
||||
|
@ -332,31 +332,31 @@ symlink.
|
|||
dependencies) is obtained by distributing the closure of a store
|
||||
derivation and specifying the option `--include-outputs`.
|
||||
|
||||
- `--references`
|
||||
- `--references`\
|
||||
Prints the set of [references](../glossary.md) of the store paths
|
||||
*paths*, that is, their immediate dependencies. (For *all*
|
||||
dependencies, use `--requisites`.)
|
||||
|
||||
- `--referrers`
|
||||
- `--referrers`\
|
||||
Prints the set of *referrers* of the store paths *paths*, that is,
|
||||
the store paths currently existing in the Nix store that refer to
|
||||
one of *paths*. Note that contrary to the references, the set of
|
||||
referrers is not constant; it can change as store paths are added or
|
||||
removed.
|
||||
|
||||
- `--referrers-closure`
|
||||
- `--referrers-closure`\
|
||||
Prints the closure of the set of store paths *paths* under the
|
||||
referrers relation; that is, all store paths that directly or
|
||||
indirectly refer to one of *paths*. These are all the path currently
|
||||
in the Nix store that are dependent on *paths*.
|
||||
|
||||
- `--deriver`; `-d`
|
||||
- `--deriver`; `-d`\
|
||||
Prints the [deriver](../glossary.md) of the store paths *paths*. If
|
||||
the path has no deriver (e.g., if it is a source file), or if the
|
||||
deriver is not known (e.g., in the case of a binary-only
|
||||
deployment), the string `unknown-deriver` is printed.
|
||||
|
||||
- `--graph`
|
||||
- `--graph`\
|
||||
Prints the references graph of the store paths *paths* in the format
|
||||
of the `dot` tool of AT\&T's [Graphviz
|
||||
package](http://www.graphviz.org/). This can be used to visualise
|
||||
|
@ -364,39 +364,39 @@ symlink.
|
|||
this to a store derivation. To obtain a runtime dependency graph,
|
||||
apply it to an output path.
|
||||
|
||||
- `--tree`
|
||||
- `--tree`\
|
||||
Prints the references graph of the store paths *paths* as a nested
|
||||
ASCII tree. References are ordered by descending closure size; this
|
||||
tends to flatten the tree, making it more readable. The query only
|
||||
recurses into a store path when it is first encountered; this
|
||||
prevents a blowup of the tree representation of the graph.
|
||||
|
||||
- `--graphml`
|
||||
- `--graphml`\
|
||||
Prints the references graph of the store paths *paths* in the
|
||||
[GraphML](http://graphml.graphdrawing.org/) file format. This can be
|
||||
used to visualise dependency graphs. To obtain a build-time
|
||||
dependency graph, apply this to a store derivation. To obtain a
|
||||
runtime dependency graph, apply it to an output path.
|
||||
|
||||
- `--binding` *name*; `-b` *name*
|
||||
- `--binding` *name*; `-b` *name*\
|
||||
Prints the value of the attribute *name* (i.e., environment
|
||||
variable) of the store derivations *paths*. It is an error for a
|
||||
derivation to not have the specified attribute.
|
||||
|
||||
- `--hash`
|
||||
- `--hash`\
|
||||
Prints the SHA-256 hash of the contents of the store paths *paths*
|
||||
(that is, the hash of the output of `nix-store --dump` on the given
|
||||
paths). Since the hash is stored in the Nix database, this is a fast
|
||||
operation.
|
||||
|
||||
- `--size`
|
||||
- `--size`\
|
||||
Prints the size in bytes of the contents of the store paths *paths*
|
||||
— to be precise, the size of the output of `nix-store --dump` on
|
||||
the given paths. Note that the actual disk space required by the
|
||||
store paths may be higher, especially on filesystems with large
|
||||
cluster sizes.
|
||||
|
||||
- `--roots`
|
||||
- `--roots`\
|
||||
Prints the garbage collector roots that point, directly or
|
||||
indirectly, at the store paths *paths*.
|
||||
|
||||
|
@ -513,7 +513,7 @@ public url or broke since the download expression was written.
|
|||
|
||||
This operation has the following options:
|
||||
|
||||
- `--recursive`
|
||||
- `--recursive`\
|
||||
Use recursive instead of flat hashing mode, used when adding
|
||||
directories to the store.
|
||||
|
||||
|
@ -540,14 +540,14 @@ being modified by non-Nix tools, or of bugs in Nix itself.
|
|||
|
||||
This operation has the following options:
|
||||
|
||||
- `--check-contents`
|
||||
- `--check-contents`\
|
||||
Checks that the contents of every valid store path has not been
|
||||
altered by computing a SHA-256 hash of the contents and comparing it
|
||||
with the hash stored in the Nix database at build time. Paths that
|
||||
have been modified are printed out. For large stores,
|
||||
`--check-contents` is obviously quite slow.
|
||||
|
||||
- `--repair`
|
||||
- `--repair`\
|
||||
If any valid path is missing from the store, or (if
|
||||
`--check-contents` is given) the contents of a valid path has been
|
||||
modified, then try to repair the path by redownloading it. See
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
Most Nix commands accept the following command-line options:
|
||||
|
||||
- `--help`
|
||||
- `--help`\
|
||||
Prints out a summary of the command syntax and exits.
|
||||
|
||||
- `--version`
|
||||
- `--version`\
|
||||
Prints out the Nix version number on standard output and exits.
|
||||
|
||||
- `--verbose` / `-v`
|
||||
- `--verbose` / `-v`\
|
||||
Increases the level of verbosity of diagnostic messages printed on
|
||||
standard error. For each Nix operation, the information printed on
|
||||
standard output is well-defined; any diagnostic information is
|
||||
|
@ -17,41 +17,41 @@ Most Nix commands accept the following command-line options:
|
|||
This option may be specified repeatedly. Currently, the following
|
||||
verbosity levels exist:
|
||||
|
||||
- 0
|
||||
- 0\
|
||||
“Errors only”: only print messages explaining why the Nix
|
||||
invocation failed.
|
||||
|
||||
- 1
|
||||
- 1\
|
||||
“Informational”: print *useful* messages about what Nix is
|
||||
doing. This is the default.
|
||||
|
||||
- 2
|
||||
- 2\
|
||||
“Talkative”: print more informational messages.
|
||||
|
||||
- 3
|
||||
- 3\
|
||||
“Chatty”: print even more informational messages.
|
||||
|
||||
- 4
|
||||
- 4\
|
||||
“Debug”: print debug information.
|
||||
|
||||
- 5
|
||||
- 5\
|
||||
“Vomit”: print vast amounts of debug information.
|
||||
|
||||
- `--quiet`
|
||||
- `--quiet`\
|
||||
Decreases the level of verbosity of diagnostic messages printed on
|
||||
standard error. This is the inverse option to `-v` / `--verbose`.
|
||||
|
||||
This option may be specified repeatedly. See the previous verbosity
|
||||
levels list.
|
||||
|
||||
- `--log-format` *format*
|
||||
- `--log-format` *format*\
|
||||
This option can be used to change the output of the log format, with
|
||||
*format* being one of:
|
||||
|
||||
- raw
|
||||
- raw\
|
||||
This is the raw format, as outputted by nix-build.
|
||||
|
||||
- internal-json
|
||||
- internal-json\
|
||||
Outputs the logs in a structured manner.
|
||||
|
||||
> **Warning**
|
||||
|
@ -60,20 +60,20 @@ Most Nix commands accept the following command-line options:
|
|||
> the error-messages (namely of the `msg`-field) can change
|
||||
> between releases.
|
||||
|
||||
- bar
|
||||
- bar\
|
||||
Only display a progress bar during the builds.
|
||||
|
||||
- bar-with-logs
|
||||
- bar-with-logs\
|
||||
Display the raw logs, with the progress bar at the bottom.
|
||||
|
||||
- `--no-build-output` / `-Q`
|
||||
- `--no-build-output` / `-Q`\
|
||||
By default, output written by builders to standard output and
|
||||
standard error is echoed to the Nix command's standard error. This
|
||||
option suppresses this behaviour. Note that the builder's standard
|
||||
output and error are always written to a log file in
|
||||
`prefix/nix/var/log/nix`.
|
||||
|
||||
- `--max-jobs` / `-j` *number*
|
||||
- `--max-jobs` / `-j` *number*\
|
||||
Sets the maximum number of build jobs that Nix will perform in
|
||||
parallel to the specified number. Specify `auto` to use the number
|
||||
of CPUs in the system. The default is specified by the `max-jobs`
|
||||
|
@ -83,7 +83,7 @@ Most Nix commands accept the following command-line options:
|
|||
Setting it to `0` disallows building on the local machine, which is
|
||||
useful when you want builds to happen only on remote builders.
|
||||
|
||||
- `--cores`
|
||||
- `--cores`\
|
||||
Sets the value of the `NIX_BUILD_CORES` environment variable in
|
||||
the invocation of builders. Builders can use this variable at
|
||||
their discretion to control the maximum amount of parallelism. For
|
||||
|
@ -94,18 +94,18 @@ Most Nix commands accept the following command-line options:
|
|||
means that the builder should use all available CPU cores in the
|
||||
system.
|
||||
|
||||
- `--max-silent-time`
|
||||
- `--max-silent-time`\
|
||||
Sets the maximum number of seconds that a builder can go without
|
||||
producing any data on standard output or standard error. The
|
||||
default is specified by the `max-silent-time` configuration
|
||||
setting. `0` means no time-out.
|
||||
|
||||
- `--timeout`
|
||||
- `--timeout`\
|
||||
Sets the maximum number of seconds that a builder can run. The
|
||||
default is specified by the `timeout` configuration setting. `0`
|
||||
means no timeout.
|
||||
|
||||
- `--keep-going` / `-k`
|
||||
- `--keep-going` / `-k`\
|
||||
Keep going in case of failed builds, to the greatest extent
|
||||
possible. That is, if building an input of some derivation fails,
|
||||
Nix will still build the other inputs, but not the derivation
|
||||
|
@ -113,13 +113,13 @@ Most Nix commands accept the following command-line options:
|
|||
for builds of substitutes), possibly killing builds in progress (in
|
||||
case of parallel or distributed builds).
|
||||
|
||||
- `--keep-failed` / `-K`
|
||||
- `--keep-failed` / `-K`\
|
||||
Specifies that in case of a build failure, the temporary directory
|
||||
(usually in `/tmp`) in which the build takes place should not be
|
||||
deleted. The path of the build directory is printed as an
|
||||
informational message.
|
||||
|
||||
- `--fallback`
|
||||
- `--fallback`\
|
||||
Whenever Nix attempts to build a derivation for which substitutes
|
||||
are known for each output path, but realising the output paths
|
||||
through the substitutes fails, fall back on building the derivation.
|
||||
|
@ -134,12 +134,12 @@ Most Nix commands accept the following command-line options:
|
|||
failure in obtaining the substitutes to lead to a full build from
|
||||
source (with the related consumption of resources).
|
||||
|
||||
- `--readonly-mode`
|
||||
- `--readonly-mode`\
|
||||
When this option is used, no attempt is made to open the Nix
|
||||
database. Most Nix operations do need database access, so those
|
||||
operations will fail.
|
||||
|
||||
- `--arg` *name* *value*
|
||||
- `--arg` *name* *value*\
|
||||
This option is accepted by `nix-env`, `nix-instantiate`,
|
||||
`nix-shell` and `nix-build`. When evaluating Nix expressions, the
|
||||
expression evaluator will automatically try to call functions that
|
||||
|
@ -170,13 +170,13 @@ Most Nix commands accept the following command-line options:
|
|||
since the argument is a Nix string literal, you have to escape the
|
||||
quotes.)
|
||||
|
||||
- `--argstr` *name* *value*
|
||||
- `--argstr` *name* *value*\
|
||||
This option is like `--arg`, only the value is not a Nix
|
||||
expression but a string. So instead of `--arg system
|
||||
\"i686-linux\"` (the outer quotes are to keep the shell happy) you
|
||||
can say `--argstr system i686-linux`.
|
||||
|
||||
- `--attr` / `-A` *attrPath*
|
||||
- `--attr` / `-A` *attrPath*\
|
||||
Select an attribute from the top-level Nix expression being
|
||||
evaluated. (`nix-env`, `nix-instantiate`, `nix-build` and
|
||||
`nix-shell` only.) The *attribute path* *attrPath* is a sequence
|
||||
|
@ -191,7 +191,7 @@ Most Nix commands accept the following command-line options:
|
|||
attribute of the fourth element of the array in the `foo` attribute
|
||||
of the top-level expression.
|
||||
|
||||
- `--expr` / `-E`
|
||||
- `--expr` / `-E`\
|
||||
Interpret the command line arguments as a list of Nix expressions to
|
||||
be parsed and evaluated, rather than as a list of file names of Nix
|
||||
expressions. (`nix-instantiate`, `nix-build` and `nix-shell` only.)
|
||||
|
@ -202,17 +202,17 @@ Most Nix commands accept the following command-line options:
|
|||
use, give your expression to the `nix-shell -p` convenience flag
|
||||
instead.
|
||||
|
||||
- `-I` *path*
|
||||
- `-I` *path*\
|
||||
Add a path to the Nix expression search path. This option may be
|
||||
given multiple times. See the `NIX_PATH` environment variable for
|
||||
information on the semantics of the Nix search path. Paths added
|
||||
through `-I` take precedence over `NIX_PATH`.
|
||||
|
||||
- `--option` *name* *value*
|
||||
- `--option` *name* *value*\
|
||||
Set the Nix configuration option *name* to *value*. This overrides
|
||||
settings in the Nix configuration file (see nix.conf5).
|
||||
|
||||
- `--repair`
|
||||
- `--repair`\
|
||||
Fix corrupted or missing store paths by redownloading or rebuilding
|
||||
them. Note that this is slow because it requires computing a
|
||||
cryptographic hash of the contents of every path in the closure of
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Derivations can declare some infrequently used optional attributes.
|
||||
|
||||
- `allowedReferences`
|
||||
- `allowedReferences`\
|
||||
The optional attribute `allowedReferences` specifies a list of legal
|
||||
references (dependencies) of the output of the builder. For example,
|
||||
|
||||
|
@ -17,7 +17,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
booting Linux don’t have accidental dependencies on other paths in
|
||||
the Nix store.
|
||||
|
||||
- `allowedRequisites`
|
||||
- `allowedRequisites`\
|
||||
This attribute is similar to `allowedReferences`, but it specifies
|
||||
the legal requisites of the whole closure, so all the dependencies
|
||||
recursively. For example,
|
||||
|
@ -30,7 +30,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
runtime dependency than `foobar`, and in addition it enforces that
|
||||
`foobar` itself doesn't introduce any other dependency itself.
|
||||
|
||||
- `disallowedReferences`
|
||||
- `disallowedReferences`\
|
||||
The optional attribute `disallowedReferences` specifies a list of
|
||||
illegal references (dependencies) of the output of the builder. For
|
||||
example,
|
||||
|
@ -42,7 +42,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
enforces that the output of a derivation cannot have a direct
|
||||
runtime dependencies on the derivation `foo`.
|
||||
|
||||
- `disallowedRequisites`
|
||||
- `disallowedRequisites`\
|
||||
This attribute is similar to `disallowedReferences`, but it
|
||||
specifies illegal requisites for the whole closure, so all the
|
||||
dependencies recursively. For example,
|
||||
|
@ -55,7 +55,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
dependency on `foobar` or any other derivation depending recursively
|
||||
on `foobar`.
|
||||
|
||||
- `exportReferencesGraph`
|
||||
- `exportReferencesGraph`\
|
||||
This attribute allows builders access to the references graph of
|
||||
their inputs. The attribute is a list of inputs in the Nix store
|
||||
whose references graph the builder needs to know. The value of
|
||||
|
@ -84,7 +84,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
with a Nix store containing the closure of a bootable NixOS
|
||||
configuration).
|
||||
|
||||
- `impureEnvVars`
|
||||
- `impureEnvVars`\
|
||||
This attribute allows you to specify a list of environment variables
|
||||
that should be passed from the environment of the calling user to
|
||||
the builder. Usually, the environment is cleared completely when the
|
||||
|
@ -112,7 +112,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
> environmental variables come from the environment of the
|
||||
> `nix-build`.
|
||||
|
||||
- `outputHash`; `outputHashAlgo`; `outputHashMode`
|
||||
- `outputHash`; `outputHashAlgo`; `outputHashMode`\
|
||||
These attributes declare that the derivation is a so-called
|
||||
*fixed-output derivation*, which means that a cryptographic hash of
|
||||
the output is already known in advance. When the build of a
|
||||
|
@ -188,7 +188,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
The `outputHashMode` attribute determines how the hash is computed.
|
||||
It must be one of the following two values:
|
||||
|
||||
- `"flat"`
|
||||
- `"flat"`\
|
||||
The output must be a non-executable regular file. If it isn’t,
|
||||
the build fails. The hash is simply computed over the contents
|
||||
of that file (so it’s equal to what Unix commands like
|
||||
|
@ -196,7 +196,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
|
||||
This is the default.
|
||||
|
||||
- `"recursive"`
|
||||
- `"recursive"`\
|
||||
The hash is computed over the NAR archive dump of the output
|
||||
(i.e., the result of [`nix-store
|
||||
--dump`](../command-ref/nix-store.md#operation---dump)). In
|
||||
|
@ -208,7 +208,15 @@ Derivations can declare some infrequently used optional attributes.
|
|||
[`nix-hash` command](../command-ref/nix-hash.md) for information
|
||||
about converting to and from base-32 notation.)
|
||||
|
||||
- `passAsFile`
|
||||
- `__contentAddressed`
|
||||
If this **experimental** attribute is set to true, then the derivation
|
||||
outputs will be stored in a content-addressed location rather than the
|
||||
traditional input-addressed one.
|
||||
This only has an effect if the `ca-derivation` experimental feature is enabled.
|
||||
|
||||
Setting this attribute also requires setting `outputHashMode` and `outputHashAlgo` like for *fixed-output derivations* (see above).
|
||||
|
||||
- `passAsFile`\
|
||||
A list of names of attributes that should be passed via files rather
|
||||
than environment variables. For example, if you have
|
||||
|
||||
|
@ -226,7 +234,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
builder, since most operating systems impose a limit on the size
|
||||
of the environment (typically, a few hundred kilobyte).
|
||||
|
||||
- `preferLocalBuild`
|
||||
- `preferLocalBuild`\
|
||||
If this attribute is set to `true` and [distributed building is
|
||||
enabled](../advanced-topics/distributed-builds.md), then, if
|
||||
possible, the derivaton will be built locally instead of forwarded
|
||||
|
@ -234,7 +242,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
where the cost of doing a download or remote build would exceed
|
||||
the cost of building locally.
|
||||
|
||||
- `allowSubstitutes`
|
||||
- `allowSubstitutes`\
|
||||
If this attribute is set to `false`, then Nix will always build this
|
||||
derivation; it will not try to substitute its outputs. This is
|
||||
useful for very trivial derivations (such as `writeText` in Nixpkgs)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Here are the constants built into the Nix expression evaluator:
|
||||
|
||||
- `builtins`
|
||||
- `builtins`\
|
||||
The set `builtins` contains all the built-in functions and values.
|
||||
You can use `builtins` to test for the availability of features in
|
||||
the Nix installation, e.g.,
|
||||
|
@ -14,7 +14,7 @@ Here are the constants built into the Nix expression evaluator:
|
|||
This allows a Nix expression to fall back gracefully on older Nix
|
||||
installations that don’t have the desired built-in function.
|
||||
|
||||
- `builtins.currentSystem`
|
||||
- `builtins.currentSystem`\
|
||||
The built-in value `currentSystem` evaluates to the Nix platform
|
||||
identifier for the Nix installation on which the expression is being
|
||||
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.
|
||||
|
|
|
@ -9,7 +9,7 @@ scope. Instead, you can access them through the `builtins` built-in
|
|||
value, which is a set that contains all built-in functions and values.
|
||||
For instance, `derivation` is also available as `builtins.derivation`.
|
||||
|
||||
- `derivation` *attrs*; `builtins.derivation` *attrs*
|
||||
- `derivation` *attrs*; `builtins.derivation` *attrs*\
|
||||
|
||||
`derivation` is described in [its own section](derivations.md).
|
||||
|
||||
|
|
|
@ -1,48 +1,48 @@
|
|||
# Glossary
|
||||
|
||||
- derivation
|
||||
- derivation\
|
||||
A description of a build action. The result of a derivation is a
|
||||
store object. Derivations are typically specified in Nix expressions
|
||||
using the [`derivation` primitive](expressions/derivations.md). These are
|
||||
translated into low-level *store derivations* (implicitly by
|
||||
`nix-env` and `nix-build`, or explicitly by `nix-instantiate`).
|
||||
|
||||
- store
|
||||
- store\
|
||||
The location in the file system where store objects live. Typically
|
||||
`/nix/store`.
|
||||
|
||||
- store path
|
||||
- store path\
|
||||
The location in the file system of a store object, i.e., an
|
||||
immediate child of the Nix store directory.
|
||||
|
||||
- store object
|
||||
- store object\
|
||||
A file that is an immediate child of the Nix store directory. These
|
||||
can be regular files, but also entire directory trees. Store objects
|
||||
can be sources (objects copied from outside of the store),
|
||||
derivation outputs (objects produced by running a build action), or
|
||||
derivations (files describing a build action).
|
||||
|
||||
- substitute
|
||||
- substitute\
|
||||
A substitute is a command invocation stored in the Nix database that
|
||||
describes how to build a store object, bypassing the normal build
|
||||
mechanism (i.e., derivations). Typically, the substitute builds the
|
||||
store object by downloading a pre-built version of the store object
|
||||
from some server.
|
||||
|
||||
- purity
|
||||
- purity\
|
||||
The assumption that equal Nix derivations when run always produce
|
||||
the same output. This cannot be guaranteed in general (e.g., a
|
||||
builder can rely on external inputs such as the network or the
|
||||
system time) but the Nix model assumes it.
|
||||
|
||||
- Nix expression
|
||||
- Nix expression\
|
||||
A high-level description of software packages and compositions
|
||||
thereof. Deploying software using Nix entails writing Nix
|
||||
expressions for your packages. Nix expressions are translated to
|
||||
derivations that are stored in the Nix store. These derivations can
|
||||
then be built.
|
||||
|
||||
- reference
|
||||
- reference\
|
||||
A store path `P` is said to have a reference to a store path `Q` if
|
||||
the store object at `P` contains the path `Q` somewhere. The
|
||||
*references* of a store path are the set of store paths to which it
|
||||
|
@ -52,11 +52,11 @@
|
|||
output paths), whereas an output path only references other output
|
||||
paths.
|
||||
|
||||
- reachable
|
||||
- reachable\
|
||||
A store path `Q` is reachable from another store path `P` if `Q`
|
||||
is in the *closure* of the *references* relation.
|
||||
|
||||
- closure
|
||||
- closure\
|
||||
The closure of a store path is the set of store paths that are
|
||||
directly or indirectly “reachable” from that store path; that is,
|
||||
it’s the closure of the path under the *references* relation. For
|
||||
|
@ -71,29 +71,29 @@
|
|||
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
|
||||
references `R` then `R` is also in the closure of `P`.
|
||||
|
||||
- output path
|
||||
- output path\
|
||||
A store path produced by a derivation.
|
||||
|
||||
- deriver
|
||||
- deriver\
|
||||
The deriver of an *output path* is the store
|
||||
derivation that built it.
|
||||
|
||||
- validity
|
||||
- validity\
|
||||
A store path is considered *valid* if it exists in the file system,
|
||||
is listed in the Nix database as being valid, and if all paths in
|
||||
its closure are also valid.
|
||||
|
||||
- user environment
|
||||
- user environment\
|
||||
An automatically generated store object that consists of a set of
|
||||
symlinks to “active” applications, i.e., other store paths. These
|
||||
are generated automatically by
|
||||
[`nix-env`](command-ref/nix-env.md). See *profiles*.
|
||||
|
||||
- profile
|
||||
- profile\
|
||||
A symlink to the current *user environment* of a user, e.g.,
|
||||
`/nix/var/nix/profiles/default`.
|
||||
|
||||
- NAR
|
||||
- NAR\
|
||||
A *N*ix *AR*chive. This is a serialisation of a path in the Nix
|
||||
store. It can contain regular files, directories and symbolic
|
||||
links. NARs are generated and unpacked using `nix-store --dump`
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
# Installing a Binary Distribution
|
||||
|
||||
If you are using Linux or macOS versions up to 10.14 (Mojave), the
|
||||
easiest way to install Nix is to run the following command:
|
||||
The easiest way to install Nix is to run the following command:
|
||||
|
||||
```console
|
||||
$ sh <(curl -L https://nixos.org/nix/install)
|
||||
```
|
||||
|
||||
If you're using macOS 10.15 (Catalina) or newer, consult [the macOS
|
||||
installation instructions](#macos-installation) before installing.
|
||||
This will run the installer interactively (causing it to explain what
|
||||
it is doing more explicitly), and perform the default "type" of install
|
||||
for your platform:
|
||||
- single-user on Linux
|
||||
- multi-user on macOS
|
||||
|
||||
As of Nix 2.1.0, the Nix installer will always default to creating a
|
||||
single-user installation, however opting in to the multi-user
|
||||
installation is highly recommended.
|
||||
> **Notes on read-only filesystem root in macOS 10.15 Catalina +**
|
||||
>
|
||||
> - It took some time to support this cleanly. You may see posts,
|
||||
> examples, and tutorials using obsolete workarounds.
|
||||
> - Supporting it cleanly made macOS installs too complex to qualify
|
||||
> as single-user, so this type is no longer supported on macOS.
|
||||
|
||||
We recommend the multi-user install if it supports your platform and
|
||||
you can authenticate with `sudo`.
|
||||
|
||||
# Single User Installation
|
||||
|
||||
|
@ -50,8 +58,8 @@ $ rm -rf /nix
|
|||
The multi-user Nix installation creates system users, and a system
|
||||
service for the Nix daemon.
|
||||
|
||||
**Supported Systems**
|
||||
- Linux running systemd, with SELinux disabled
|
||||
|
||||
- macOS
|
||||
|
||||
You can instruct the installer to perform a multi-user installation on
|
||||
|
@ -96,165 +104,28 @@ sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
|||
There may also be references to Nix in `/etc/profile`, `/etc/bashrc`,
|
||||
and `/etc/zshrc` which you may remove.
|
||||
|
||||
# macOS Installation
|
||||
# macOS Installation <a name="sect-macos-installation-change-store-prefix"></a><a name="sect-macos-installation-encrypted-volume"></a><a name="sect-macos-installation-symlink"></a><a name="sect-macos-installation-recommended-notes"></a>
|
||||
<!-- Note: anchors above to catch permalinks to old explanations -->
|
||||
|
||||
Starting with macOS 10.15 (Catalina), the root filesystem is read-only.
|
||||
This means `/nix` can no longer live on your system volume, and that
|
||||
you'll need a workaround to install Nix.
|
||||
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
|
||||
also re-run a new installer to convert your existing setup.
|
||||
|
||||
The recommended approach, which creates an unencrypted APFS volume for
|
||||
your Nix store and a "synthetic" empty directory to mount it over at
|
||||
`/nix`, is least likely to impair Nix or your system.
|
||||
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
|
||||
this to run the installer, but it may help if you run into trouble:
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> With all separate-volume approaches, it's possible something on your
|
||||
> system (particularly daemons/services and restored apps) may need
|
||||
> access to your Nix store before the volume is mounted. Adding
|
||||
> additional encryption makes this more likely.
|
||||
|
||||
If you're using a recent Mac with a [T2
|
||||
chip](https://www.apple.com/euro/mac/shared/docs/Apple_T2_Security_Chip_Overview.pdf),
|
||||
your drive will still be encrypted at rest (in which case "unencrypted"
|
||||
is a bit of a misnomer). To use this approach, just install Nix with:
|
||||
|
||||
```console
|
||||
$ sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume
|
||||
```
|
||||
|
||||
If you don't like the sound of this, you'll want to weigh the other
|
||||
approaches and tradeoffs detailed in this section.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> All of the known workarounds have drawbacks, but we hope better
|
||||
> solutions will be available in the future. Some that we have our eye
|
||||
> on are:
|
||||
>
|
||||
> 1. A true firmlink would enable the Nix store to live on the primary
|
||||
> data volume without the build problems caused by the symlink
|
||||
> approach. End users cannot currently create true firmlinks.
|
||||
>
|
||||
> 2. If the Nix store volume shared FileVault encryption with the
|
||||
> primary data volume (probably by using the same volume group and
|
||||
> role), FileVault encryption could be easily supported by the
|
||||
> installer without requiring manual setup by each user.
|
||||
|
||||
## Change the Nix store path prefix
|
||||
|
||||
Changing the default prefix for the Nix store is a simple approach which
|
||||
enables you to leave it on your root volume, where it can take full
|
||||
advantage of FileVault encryption if enabled. Unfortunately, this
|
||||
approach also opts your device out of some benefits that are enabled by
|
||||
using the same prefix across systems:
|
||||
|
||||
- Your system won't be able to take advantage of the binary cache
|
||||
(unless someone is able to stand up and support duplicate caching
|
||||
infrastructure), which means you'll spend more time waiting for
|
||||
builds.
|
||||
|
||||
- It's harder to build and deploy packages to Linux systems.
|
||||
|
||||
It would also possible (and often requested) to just apply this change
|
||||
ecosystem-wide, but it's an intrusive process that has side effects we
|
||||
want to avoid for now.
|
||||
|
||||
## Use a separate encrypted volume
|
||||
|
||||
If you like, you can also add encryption to the recommended approach
|
||||
taken by the installer. You can do this by pre-creating an encrypted
|
||||
volume before you run the installer--or you can run the installer and
|
||||
encrypt the volume it creates later.
|
||||
|
||||
In either case, adding encryption to a second volume isn't quite as
|
||||
simple as enabling FileVault for your boot volume. Before you dive in,
|
||||
there are a few things to weigh:
|
||||
|
||||
1. The additional volume won't be encrypted with your existing
|
||||
FileVault key, so you'll need another mechanism to decrypt the
|
||||
volume.
|
||||
|
||||
2. You can store the password in Keychain to automatically decrypt the
|
||||
volume on boot--but it'll have to wait on Keychain and may not mount
|
||||
before your GUI apps restore. If any of your launchd agents or apps
|
||||
depend on Nix-installed software (for example, if you use a
|
||||
Nix-installed login shell), the restore may fail or break.
|
||||
|
||||
On a case-by-case basis, you may be able to work around this problem
|
||||
by using `wait4path` to block execution until your executable is
|
||||
available.
|
||||
|
||||
It's also possible to decrypt and mount the volume earlier with a
|
||||
login hook--but this mechanism appears to be deprecated and its
|
||||
future is unclear.
|
||||
|
||||
3. You can hard-code the password in the clear, so that your store
|
||||
volume can be decrypted before Keychain is available.
|
||||
|
||||
If you are comfortable navigating these tradeoffs, you can encrypt the
|
||||
volume with something along the lines of:
|
||||
|
||||
```console
|
||||
$ diskutil apfs enableFileVault /nix -user disk
|
||||
```
|
||||
|
||||
## Symlink the Nix store to a custom location
|
||||
|
||||
Another simple approach is using `/etc/synthetic.conf` to symlink the
|
||||
Nix store to the data volume. This option also enables your store to
|
||||
share any configured FileVault encryption. Unfortunately, builds that
|
||||
resolve the symlink may leak the canonical path or even fail.
|
||||
|
||||
Because of these downsides, we can't recommend this approach.
|
||||
|
||||
## Notes on the recommended approach
|
||||
|
||||
This section goes into a little more detail on the recommended approach.
|
||||
You don't need to understand it to run the installer, but it can serve
|
||||
as a helpful reference if you run into trouble.
|
||||
|
||||
1. In order to compose user-writable locations into the new read-only
|
||||
system root, Apple introduced a new concept called `firmlinks`,
|
||||
which it describes as a "bi-directional wormhole" between two
|
||||
filesystems. You can see the current firmlinks in
|
||||
`/usr/share/firmlinks`. Unfortunately, firmlinks aren't (currently?)
|
||||
user-configurable.
|
||||
|
||||
For special cases like NFS mount points or package manager roots,
|
||||
[synthetic.conf(5)](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man5/synthetic.conf.5.html)
|
||||
supports limited user-controlled file-creation (of symlinks, and
|
||||
synthetic empty directories) at `/`. To create a synthetic empty
|
||||
directory for mounting at `/nix`, add the following line to
|
||||
`/etc/synthetic.conf` (create it if necessary):
|
||||
|
||||
nix
|
||||
|
||||
2. This configuration is applied at boot time, but you can use
|
||||
`apfs.util` to trigger creation (not deletion) of new entries
|
||||
without a reboot:
|
||||
|
||||
```console
|
||||
$ /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B
|
||||
```
|
||||
|
||||
3. Create the new APFS volume with diskutil:
|
||||
|
||||
```console
|
||||
$ sudo diskutil apfs addVolume diskX APFS 'Nix Store' -mountpoint /nix
|
||||
```
|
||||
|
||||
4. Using `vifs`, add the new mount to `/etc/fstab`. If it doesn't
|
||||
already have other entries, it should look something like:
|
||||
|
||||
#
|
||||
# Warning - this file should only be modified with vifs(8)
|
||||
#
|
||||
# Failure to do so is unsupported and may be destructive.
|
||||
#
|
||||
LABEL=Nix\040Store /nix apfs rw,nobrowse
|
||||
|
||||
The nobrowse setting will keep Spotlight from indexing this volume,
|
||||
and keep it from showing up on your desktop.
|
||||
- create a new APFS volume for your Nix store
|
||||
- update `/etc/synthetic.conf` to direct macOS to create a "synthetic"
|
||||
empty root directory to mount your volume
|
||||
- specify mount options for the volume in `/etc/fstab`
|
||||
- if you have FileVault enabled
|
||||
- generate an encryption password
|
||||
- put it in your system Keychain
|
||||
- use it to encrypt the volume
|
||||
- create a system LaunchDaemon to mount this volume early enough in the
|
||||
boot process to avoid problems loading or restoring any programs that
|
||||
need access to your Nix store
|
||||
|
||||
# Installing a pinned Nix version from a URL
|
||||
|
||||
|
|
|
@ -7,17 +7,17 @@ cache mechanism that Nix usually uses to fetch prebuilt binaries from
|
|||
|
||||
The following options can be specified as URL parameters to the S3 URL:
|
||||
|
||||
- `profile`
|
||||
- `profile`\
|
||||
The name of the AWS configuration profile to use. By default Nix
|
||||
will use the `default` profile.
|
||||
|
||||
- `region`
|
||||
- `region`\
|
||||
The region of the S3 bucket. `us–east-1` by default.
|
||||
|
||||
If your bucket is not in `us–east-1`, you should always explicitly
|
||||
specify the region parameter.
|
||||
|
||||
- `endpoint`
|
||||
- `endpoint`\
|
||||
The URL to your S3-compatible service, for when not using Amazon S3.
|
||||
Do not specify this value if you're using Amazon S3.
|
||||
|
||||
|
@ -26,7 +26,7 @@ The following options can be specified as URL parameters to the S3 URL:
|
|||
> This endpoint must support HTTPS and will use path-based
|
||||
> addressing instead of virtual host based addressing.
|
||||
|
||||
- `scheme`
|
||||
- `scheme`\
|
||||
The scheme used for S3 requests, `https` (default) or `http`. This
|
||||
option allows you to disable HTTPS for binary caches which don't
|
||||
support it.
|
||||
|
|
26
flake.lock
26
flake.lock
|
@ -1,22 +1,40 @@
|
|||
{
|
||||
"nodes": {
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1617481909,
|
||||
"narHash": "sha256-SqnfOFuLuVRRNeVJr1yeEPJue/qWoCp5N6o5Kr///p4=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "148f9b2f586c41b7e36e73009db43ea68c7a1a4d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "kristapsdz",
|
||||
"ref": "VERSION_0_8_4",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1614309161,
|
||||
"narHash": "sha256-93kRxDPyEW9QIpxU71kCaV1r+hgOgP6/aVgC7vvO8IU=",
|
||||
"lastModified": 1622593737,
|
||||
"narHash": "sha256-9loxFJg85AbzJrSkU4pE/divZ1+zOxDy2FSjlrufCB8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0e499fde7af3c28d63e9b13636716b86c3162b93",
|
||||
"rev": "bb8a5e54845012ed1375ffd5f317d2fdf434b20e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-20.09-small",
|
||||
"ref": "nixos-21.05-small",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"lowdown-src": "lowdown-src",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
|
|
77
flake.nix
77
flake.nix
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
description = "The purely functional package manager";
|
||||
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-20.09-small";
|
||||
#inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-21.05-small";
|
||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown/VERSION_0_8_4"; flake = false; };
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
outputs = { self, nixpkgs, lowdown-src }:
|
||||
|
||||
let
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
|||
|
||||
linux64BitSystems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
|
||||
systems = linuxSystems ++ [ "x86_64-darwin" ];
|
||||
systems = linuxSystems ++ [ "x86_64-darwin" "aarch64-darwin" ];
|
||||
|
||||
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
||||
|
||||
|
@ -80,11 +80,12 @@
|
|||
buildPackages.git
|
||||
buildPackages.mercurial
|
||||
buildPackages.jq
|
||||
] ++ lib.optional stdenv.hostPlatform.isLinux buildPackages.utillinuxMinimal;
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
|
||||
|
||||
buildDeps =
|
||||
[ curl
|
||||
bzip2 xz brotli zlib editline
|
||||
bzip2 xz brotli editline
|
||||
openssl sqlite
|
||||
libarchive
|
||||
boost
|
||||
|
@ -92,7 +93,7 @@
|
|||
lowdown
|
||||
gmock
|
||||
]
|
||||
++ lib.optional stdenv.isLinux libseccomp
|
||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
|
||||
|
||||
|
@ -146,12 +147,46 @@
|
|||
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
testNixVersions = pkgs: client: daemon: with commonDeps pkgs; pkgs.stdenv.mkDerivation {
|
||||
NIX_DAEMON_PACKAGE = daemon;
|
||||
NIX_CLIENT_PACKAGE = client;
|
||||
# Must keep this name short as OSX has a rather strict limit on the
|
||||
# socket path length, and this name appears in the path of the
|
||||
# nix-daemon socket used in the tests
|
||||
name = "nix-tests";
|
||||
inherit version;
|
||||
|
||||
src = self;
|
||||
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps ++ awsDeps;
|
||||
propagatedBuildInputs = propagatedDeps;
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
dontBuild = true;
|
||||
doInstallCheck = true;
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
'';
|
||||
installCheckPhase = "make installcheck";
|
||||
|
||||
};
|
||||
|
||||
in {
|
||||
|
||||
# A Nixpkgs overlay that overrides the 'nix' and
|
||||
# 'nix.perl-bindings' packages.
|
||||
overlay = final: prev: {
|
||||
|
||||
# An older version of Nix to test against when using the daemon.
|
||||
# Currently using `nixUnstable` as the stable one doesn't respect
|
||||
# `NIX_DAEMON_SOCKET_PATH` which is needed for the tests.
|
||||
nixStable = prev.nix;
|
||||
|
||||
nix = with final; with commonDeps pkgs; stdenv.mkDerivation {
|
||||
name = "nix-${version}";
|
||||
inherit version;
|
||||
|
@ -201,6 +236,8 @@
|
|||
|
||||
separateDebugInfo = true;
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
passthru.perl-bindings = with final; stdenv.mkDerivation {
|
||||
name = "nix-perl-${version}";
|
||||
|
||||
|
@ -221,7 +258,8 @@
|
|||
boost
|
||||
nlohmann_json
|
||||
]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium;
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
|
||||
|
||||
configureFlags = ''
|
||||
--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
|
||||
|
@ -236,21 +274,23 @@
|
|||
};
|
||||
|
||||
lowdown = with final; stdenv.mkDerivation rec {
|
||||
name = "lowdown-0.8.0";
|
||||
name = "lowdown-0.8.4";
|
||||
|
||||
/*
|
||||
src = fetchurl {
|
||||
url = "https://kristaps.bsd.lv/lowdown/snapshots/${name}.tar.gz";
|
||||
hash = "sha512-U9WeGoInT9vrawwa57t6u9dEdRge4/P+0wLxmQyOL9nhzOEUU2FRz2Be9H0dCjYE7p2v3vCXIYk40M+jjULATw==";
|
||||
};
|
||||
*/
|
||||
|
||||
#src = lowdown-src;
|
||||
src = lowdown-src;
|
||||
|
||||
outputs = [ "out" "bin" "dev" ];
|
||||
|
||||
nativeBuildInputs = [ buildPackages.which ];
|
||||
|
||||
configurePhase =
|
||||
''
|
||||
configurePhase = ''
|
||||
${if (stdenv.isDarwin && stdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
||||
./configure \
|
||||
PREFIX=${placeholder "dev"} \
|
||||
BINDIR=${placeholder "bin"}/bin
|
||||
|
@ -353,7 +393,7 @@
|
|||
# to https://nixos.org/nix/install. It downloads the binary
|
||||
# tarball for the user's system and calls the second half of the
|
||||
# installation script.
|
||||
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ];
|
||||
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||||
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" ];
|
||||
|
||||
# Line coverage analysis.
|
||||
|
@ -439,6 +479,15 @@
|
|||
checks = forAllSystems (system: {
|
||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||
installTests =
|
||||
let pkgs = nixpkgsFor.${system}; in
|
||||
pkgs.runCommand "install-tests" {
|
||||
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
|
||||
againstCurrentUnstable = testNixVersions pkgs pkgs.nix pkgs.nixUnstable;
|
||||
# Disabled because the latest stable version doesn't handle
|
||||
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
||||
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
||||
} "touch $out";
|
||||
});
|
||||
|
||||
packages = forAllSystems (system: {
|
||||
|
@ -479,6 +528,8 @@
|
|||
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
stripAllList = ["bin"];
|
||||
|
||||
strictDeps = true;
|
||||
};
|
||||
} // builtins.listToAttrs (map (crossSystem: {
|
||||
name = "nix-${crossSystem}";
|
||||
|
|
|
@ -133,20 +133,8 @@ for my $fn (glob "$tmpDir/*") {
|
|||
|
||||
exit if $version =~ /pre/;
|
||||
|
||||
# Update Nixpkgs in a very hacky way.
|
||||
# Update nix-fallback-paths.nix.
|
||||
system("cd $nixpkgsDir && git pull") == 0 or die;
|
||||
my $oldName = `nix-instantiate --eval $nixpkgsDir -A nix.name`; chomp $oldName;
|
||||
my $oldHash = `nix-instantiate --eval $nixpkgsDir -A nix.src.outputHash`; chomp $oldHash;
|
||||
print STDERR "old stable version in Nixpkgs = $oldName / $oldHash\n";
|
||||
|
||||
my $fn = "$nixpkgsDir/pkgs/tools/package-management/nix/default.nix";
|
||||
my $oldFile = read_file($fn);
|
||||
$oldFile =~ s/$oldName/"$releaseName"/g;
|
||||
$oldFile =~ s/$oldHash/"$tarballHash"/g;
|
||||
write_file($fn, $oldFile);
|
||||
|
||||
$oldName =~ s/nix-//g;
|
||||
$oldName =~ s/"//g;
|
||||
|
||||
sub getStorePath {
|
||||
my ($jobName) = @_;
|
||||
|
@ -167,7 +155,7 @@ write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
|||
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
|
||||
"}\n");
|
||||
|
||||
system("cd $nixpkgsDir && git commit -a -m 'nix: $oldName -> $version'") == 0 or die;
|
||||
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
|
||||
|
||||
# Update the "latest" symlink.
|
||||
$channelsBucket->add_key(
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<array>
|
||||
<string>/bin/sh</string>
|
||||
<string>-c</string>
|
||||
<string>/bin/wait4path /nix/var/nix/profiles/default/bin/nix-daemon && /nix/var/nix/profiles/default/bin/nix-daemon</string>
|
||||
<string>/bin/wait4path /nix/var/nix/profiles/default/bin/nix-daemon && exec /nix/var/nix/profiles/default/bin/nix-daemon</string>
|
||||
</array>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/nix-daemon.log</string>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#compdef nix
|
||||
|
||||
function _nix() {
|
||||
local ifs_bk="$IFS"
|
||||
local input=("${(Q)words[@]}")
|
||||
|
@ -18,4 +20,4 @@ function _nix() {
|
|||
_describe 'nix' suggestions
|
||||
}
|
||||
|
||||
compdef _nix nix
|
||||
_nix "$@"
|
||||
|
|
1
misc/zsh/local.mk
Normal file
1
misc/zsh/local.mk
Normal file
|
@ -0,0 +1 @@
|
|||
$(eval $(call install-file-as, $(d)/completion.zsh, $(datarootdir)/zsh/site-functions/_nix, 0644))
|
|
@ -8,8 +8,13 @@ endif
|
|||
|
||||
libnixrust_PATH := $(d)/target/$(RUST_DIR)/libnixrust.$(SO_EXT)
|
||||
libnixrust_INSTALL_PATH := $(libdir)/libnixrust.$(SO_EXT)
|
||||
libnixrust_LDFLAGS_USE := -L$(d)/target/$(RUST_DIR) -lnixrust -ldl
|
||||
libnixrust_LDFLAGS_USE_INSTALLED := -L$(libdir) -lnixrust -ldl
|
||||
libnixrust_LDFLAGS_USE := -L$(d)/target/$(RUST_DIR) -lnixrust
|
||||
libnixrust_LDFLAGS_USE_INSTALLED := -L$(libdir) -lnixrust
|
||||
|
||||
ifeq ($(OS), Linux)
|
||||
libnixrust_LDFLAGS_USE += -ldl
|
||||
libnixrust_LDFLAGS_USE_INSTALLED += -ldl
|
||||
endif
|
||||
|
||||
ifeq ($(OS), Darwin)
|
||||
libnixrust_BUILD_FLAGS = NIX_LDFLAGS="-undefined dynamic_lookup"
|
||||
|
|
46
scripts/bigsur-nixbld-user-migration.sh
Executable file
46
scripts/bigsur-nixbld-user-migration.sh
Executable file
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
((NEW_NIX_FIRST_BUILD_UID=301))
|
||||
|
||||
id_available(){
|
||||
dscl . list /Users UniqueID | grep -E '\b'$1'\b' >/dev/null
|
||||
}
|
||||
|
||||
change_nixbld_names_and_ids(){
|
||||
local name uid next_id
|
||||
((next_id=NEW_NIX_FIRST_BUILD_UID))
|
||||
echo "Attempting to migrate nixbld users."
|
||||
echo "Each user should change from nixbld# to _nixbld#"
|
||||
echo "and their IDs relocated to $next_id+"
|
||||
while read -r name uid; do
|
||||
echo " Checking $name (uid: $uid)"
|
||||
# iterate for a clean ID
|
||||
while id_available "$next_id"; do
|
||||
((next_id++))
|
||||
if ((next_id >= 400)); then
|
||||
echo "We've hit UID 400 without placing all of your users :("
|
||||
echo "You should use the commands in this script as a starting"
|
||||
echo "point to review your UID-space and manually move the"
|
||||
echo "remaining users (or delete them, if you don't need them)."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $name == _* ]]; then
|
||||
echo " It looks like $name has already been renamed--skipping."
|
||||
else
|
||||
# first 3 are cleanup, it's OK if they aren't here
|
||||
sudo dscl . delete /Users/$name dsAttrTypeNative:_writers_passwd &>/dev/null || true
|
||||
sudo dscl . change /Users/$name NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true
|
||||
# remove existing user from group
|
||||
sudo dseditgroup -o edit -t user -d $name nixbld || true
|
||||
sudo dscl . change /Users/$name UniqueID $uid $next_id
|
||||
sudo dscl . change /Users/$name RecordName $name _$name
|
||||
# add renamed user to group
|
||||
sudo dseditgroup -o edit -t user -a _$name nixbld
|
||||
echo " $name migrated to _$name (uid: $next_id)"
|
||||
fi
|
||||
done < <(dscl . list /Users UniqueID | grep nixbld | sort -n -k2)
|
||||
}
|
||||
|
||||
change_nixbld_names_and_ids
|
|
@ -1,29 +1,258 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
root_disk() {
|
||||
diskutil info -plist /
|
||||
# I'm a little agnostic on the choices, but supporting a wide
|
||||
# slate of uses for now, including:
|
||||
# - import-only: `. create-darwin-volume.sh no-main[ ...]`
|
||||
# - legacy: `./create-darwin-volume.sh` or `. create-darwin-volume.sh`
|
||||
# (both will run main())
|
||||
# - external alt-routine: `./create-darwin-volume.sh no-main func[ ...]`
|
||||
if [ "${1-}" = "no-main" ]; then
|
||||
shift
|
||||
readonly _CREATE_VOLUME_NO_MAIN=1
|
||||
else
|
||||
readonly _CREATE_VOLUME_NO_MAIN=0
|
||||
# declare some things we expect to inherit from install-multi-user
|
||||
# I don't love this (because it's a bit of a kludge).
|
||||
#
|
||||
# CAUTION: (Dec 19 2020)
|
||||
# This is a stopgap. It doesn't cover the full slate of
|
||||
# identifiers we inherit--just those necessary to:
|
||||
# - avoid breaking direct invocations of this script (here/now)
|
||||
# - avoid hard-to-reverse structural changes before the call to rm
|
||||
# single-user support is verified
|
||||
#
|
||||
# In the near-mid term, I (personally) think we should:
|
||||
# - decide to deprecate the direct call and add a notice
|
||||
# - fold all of this into install-darwin-multi-user.sh
|
||||
# - intentionally remove the old direct-invocation form (kill the
|
||||
# routine, replace this script w/ deprecation notice and a note
|
||||
# on the remove-after date)
|
||||
#
|
||||
readonly NIX_ROOT="${NIX_ROOT:-/nix}"
|
||||
|
||||
_sudo() {
|
||||
shift # throw away the 'explanation'
|
||||
/usr/bin/sudo "$@"
|
||||
}
|
||||
failure() {
|
||||
if [ "$*" = "" ]; then
|
||||
cat
|
||||
else
|
||||
echo "$@"
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
task() {
|
||||
echo "$@"
|
||||
}
|
||||
fi
|
||||
|
||||
# i.e., "disk1"
|
||||
# usually "disk1"
|
||||
root_disk_identifier() {
|
||||
diskutil info -plist / | xmllint --xpath "/plist/dict/key[text()='ParentWholeDisk']/following-sibling::string[1]/text()" -
|
||||
# For performance (~10ms vs 280ms) I'm parsing 'diskX' from stat output
|
||||
# (~diskXsY)--but I'm retaining the more-semantic approach since
|
||||
# it documents intent better.
|
||||
# /usr/sbin/diskutil info -plist / | xmllint --xpath "/plist/dict/key[text()='ParentWholeDisk']/following-sibling::string[1]/text()" -
|
||||
#
|
||||
local special_device
|
||||
special_device="$(/usr/bin/stat -f "%Sd" /)"
|
||||
echo "${special_device%s[0-9]*}"
|
||||
}
|
||||
|
||||
find_nix_volume() {
|
||||
diskutil apfs list -plist "$1" | xmllint --xpath "(/plist/dict/array/dict/key[text()='Volumes']/following-sibling::array/dict/key[text()='Name']/following-sibling::string[starts-with(translate(text(),'N','n'),'nix')]/text())[1]" - 2>/dev/null || true
|
||||
# make it easy to play w/ 'Case-sensitive APFS'
|
||||
readonly NIX_VOLUME_FS="${NIX_VOLUME_FS:-APFS}"
|
||||
readonly NIX_VOLUME_LABEL="${NIX_VOLUME_LABEL:-Nix Store}"
|
||||
# Strongly assuming we'll make a volume on the device / is on
|
||||
# But you can override NIX_VOLUME_USE_DISK to create it on some other device
|
||||
readonly NIX_VOLUME_USE_DISK="${NIX_VOLUME_USE_DISK:-$(root_disk_identifier)}"
|
||||
NIX_VOLUME_USE_SPECIAL="${NIX_VOLUME_USE_SPECIAL:-}"
|
||||
NIX_VOLUME_USE_UUID="${NIX_VOLUME_USE_UUID:-}"
|
||||
readonly NIX_VOLUME_MOUNTD_DEST="${NIX_VOLUME_MOUNTD_DEST:-/Library/LaunchDaemons/org.nixos.darwin-store.plist}"
|
||||
|
||||
if /usr/bin/fdesetup isactive >/dev/null; then
|
||||
test_filevault_in_use() { return 0; }
|
||||
# no readonly; we may modify if user refuses from cure_volume
|
||||
NIX_VOLUME_DO_ENCRYPT="${NIX_VOLUME_DO_ENCRYPT:-1}"
|
||||
else
|
||||
test_filevault_in_use() { return 1; }
|
||||
NIX_VOLUME_DO_ENCRYPT="${NIX_VOLUME_DO_ENCRYPT:-0}"
|
||||
fi
|
||||
|
||||
should_encrypt_volume() {
|
||||
test_filevault_in_use && (( NIX_VOLUME_DO_ENCRYPT == 1 ))
|
||||
}
|
||||
|
||||
substep() {
|
||||
printf " %s\n" "" "- $1" "" "${@:2}"
|
||||
}
|
||||
|
||||
|
||||
volumes_labeled() {
|
||||
local label="$1"
|
||||
xsltproc --novalid --stringparam label "$label" - <(/usr/sbin/ioreg -ra -c "AppleAPFSVolume") <<'EOF'
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
||||
<xsl:output method="text"/>
|
||||
<xsl:template match="/">
|
||||
<xsl:apply-templates select="/plist/array/dict/key[text()='IORegistryEntryName']/following-sibling::*[1][text()=$label]/.."/>
|
||||
</xsl:template>
|
||||
<xsl:template match="dict">
|
||||
<xsl:apply-templates match="string" select="key[text()='BSD Name']/following-sibling::*[1]"/>
|
||||
<xsl:text>=</xsl:text>
|
||||
<xsl:apply-templates match="string" select="key[text()='UUID']/following-sibling::*[1]"/>
|
||||
<xsl:text>
</xsl:text>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
EOF
|
||||
# I cut label out of the extracted values, but here it is for reference:
|
||||
# <xsl:apply-templates match="string" select="key[text()='IORegistryEntryName']/following-sibling::*[1]"/>
|
||||
# <xsl:text>=</xsl:text>
|
||||
}
|
||||
|
||||
right_disk() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
[[ "$volume_special" == "$NIX_VOLUME_USE_DISK"s* ]]
|
||||
}
|
||||
|
||||
right_volume() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
# if set, it must match; otherwise ensure it's on the right disk
|
||||
if [ -z "$NIX_VOLUME_USE_SPECIAL" ]; then
|
||||
if right_disk "$volume_special"; then
|
||||
NIX_VOLUME_USE_SPECIAL="$volume_special" # latch on
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
[ "$volume_special" = "$NIX_VOLUME_USE_SPECIAL" ]
|
||||
fi
|
||||
}
|
||||
|
||||
right_uuid() {
|
||||
local volume_uuid="$1"
|
||||
# if set, it must match; otherwise allow
|
||||
if [ -z "$NIX_VOLUME_USE_UUID" ]; then
|
||||
NIX_VOLUME_USE_UUID="$volume_uuid" # latch on
|
||||
return 0
|
||||
else
|
||||
[ "$volume_uuid" = "$NIX_VOLUME_USE_UUID" ]
|
||||
fi
|
||||
}
|
||||
|
||||
cure_volumes() {
|
||||
local found volume special uuid
|
||||
# loop just in case they have more than one volume
|
||||
# (nothing stops you from doing this)
|
||||
for volume in $(volumes_labeled "$NIX_VOLUME_LABEL"); do
|
||||
# CAUTION: this could (maybe) be a more normal read
|
||||
# loop like:
|
||||
# while IFS== read -r special uuid; do
|
||||
# # ...
|
||||
# done <<<"$(volumes_labeled "$NIX_VOLUME_LABEL")"
|
||||
#
|
||||
# I did it with for to skirt a problem with the obvious
|
||||
# pattern replacing stdin and causing user prompts
|
||||
# inside (which also use read and access stdin) to skip
|
||||
#
|
||||
# If there's an existing encrypted volume we can't find
|
||||
# in keychain, the user never gets prompted to delete
|
||||
# the volume, and the install fails.
|
||||
#
|
||||
# If you change this, a human needs to test a very
|
||||
# specific scenario: you already have an encrypted
|
||||
# Nix Store volume, and have deleted its credential
|
||||
# from keychain. Ensure the script asks you if it can
|
||||
# delete the volume, and then prompts for your sudo
|
||||
# password to confirm.
|
||||
#
|
||||
# shellcheck disable=SC1097
|
||||
IFS== read -r special uuid <<< "$volume"
|
||||
# take the first one that's on the right disk
|
||||
if [ -z "${found:-}" ]; then
|
||||
if right_volume "$special" && right_uuid "$uuid"; then
|
||||
cure_volume "$special" "$uuid"
|
||||
found="${special} (${uuid})"
|
||||
else
|
||||
warning <<EOF
|
||||
Ignoring ${special} (${uuid}) because I am looking for:
|
||||
disk=${NIX_VOLUME_USE_DISK} special=${NIX_VOLUME_USE_SPECIAL:-${NIX_VOLUME_USE_DISK}sX} uuid=${NIX_VOLUME_USE_UUID:-any}
|
||||
EOF
|
||||
# TODO: give chance to delete if ! headless?
|
||||
fi
|
||||
else
|
||||
warning <<EOF
|
||||
Ignoring ${special} (${uuid}), already found target: $found
|
||||
EOF
|
||||
# TODO reminder? I feel like I want one
|
||||
# idiom that reminds some warnings, or warns
|
||||
# some reminders?
|
||||
# TODO: if ! headless, chance to delete?
|
||||
fi
|
||||
done
|
||||
if [ -z "${found:-}" ]; then
|
||||
readonly NIX_VOLUME_USE_SPECIAL NIX_VOLUME_USE_UUID
|
||||
fi
|
||||
}
|
||||
|
||||
volume_encrypted() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
# Trying to match the first line of output; known first lines:
|
||||
# No cryptographic users for <special>
|
||||
# Cryptographic user for <special> (1 found)
|
||||
# Cryptographic users for <special> (2 found)
|
||||
/usr/sbin/diskutil apfs listCryptoUsers -plist "$volume_special" | /usr/bin/grep -q APFSCryptoUserUUID
|
||||
}
|
||||
|
||||
test_fstab() {
|
||||
grep -q "/nix apfs rw" /etc/fstab 2>/dev/null
|
||||
/usr/bin/grep -q "$NIX_ROOT apfs rw" /etc/fstab 2>/dev/null
|
||||
}
|
||||
|
||||
test_nix_symlink() {
|
||||
[ -L "/nix" ] || grep -q "^nix." /etc/synthetic.conf 2>/dev/null
|
||||
test_nix_root_is_symlink() {
|
||||
[ -L "$NIX_ROOT" ]
|
||||
}
|
||||
|
||||
test_synthetic_conf() {
|
||||
grep -q "^nix$" /etc/synthetic.conf 2>/dev/null
|
||||
test_synthetic_conf_either(){
|
||||
/usr/bin/grep -qE "^${NIX_ROOT:1}($|\t.{3,}$)" /etc/synthetic.conf 2>/dev/null
|
||||
}
|
||||
|
||||
test_synthetic_conf_mountable() {
|
||||
/usr/bin/grep -q "^${NIX_ROOT:1}$" /etc/synthetic.conf 2>/dev/null
|
||||
}
|
||||
|
||||
test_synthetic_conf_symlinked() {
|
||||
/usr/bin/grep -qE "^${NIX_ROOT:1}\t.{3,}$" /etc/synthetic.conf 2>/dev/null
|
||||
}
|
||||
|
||||
test_nix_volume_mountd_installed() {
|
||||
test -e "$NIX_VOLUME_MOUNTD_DEST"
|
||||
}
|
||||
|
||||
# current volume password
|
||||
test_keychain_by_uuid() {
|
||||
local volume_uuid="$1"
|
||||
# Note: doesn't need sudo just to check; doesn't output pw
|
||||
security find-generic-password -s "$volume_uuid" &>/dev/null
|
||||
}
|
||||
|
||||
get_volume_pass() {
|
||||
local volume_uuid="$1"
|
||||
_sudo \
|
||||
"to confirm keychain has a password that unlocks this volume" \
|
||||
security find-generic-password -s "$volume_uuid" -w
|
||||
}
|
||||
|
||||
verify_volume_pass() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
local volume_uuid="$2"
|
||||
/usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid"
|
||||
}
|
||||
|
||||
volume_pass_works() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
local volume_uuid="$2"
|
||||
get_volume_pass "$volume_uuid" | verify_volume_pass "$volume_special" "$volume_uuid"
|
||||
}
|
||||
|
||||
# Create the paths defined in synthetic.conf, saving us a reboot.
|
||||
|
@ -41,129 +270,570 @@ create_synthetic_objects(){
|
|||
}
|
||||
|
||||
test_nix() {
|
||||
test -d "/nix"
|
||||
test -d "$NIX_ROOT"
|
||||
}
|
||||
|
||||
test_t2_chip_present(){
|
||||
# Use xartutil to see if system has a t2 chip.
|
||||
test_voldaemon() {
|
||||
test -f "$NIX_VOLUME_MOUNTD_DEST"
|
||||
}
|
||||
|
||||
generate_mount_command() {
|
||||
local cmd_type="$1" # encrypted|unencrypted
|
||||
local volume_uuid mountpoint cmd=()
|
||||
printf -v volume_uuid "%q" "$2"
|
||||
printf -v mountpoint "%q" "$NIX_ROOT"
|
||||
|
||||
case "$cmd_type" in
|
||||
encrypted)
|
||||
cmd=(/bin/sh -c "/usr/bin/security find-generic-password -s '$volume_uuid' -w | /usr/sbin/diskutil apfs unlockVolume '$volume_uuid' -mountpoint '$mountpoint' -stdinpassphrase");;
|
||||
unencrypted)
|
||||
cmd=(/usr/sbin/diskutil mount -mountPoint "$mountpoint" "$volume_uuid");;
|
||||
*)
|
||||
failure "Invalid first arg $cmd_type to generate_mount_command";;
|
||||
esac
|
||||
|
||||
printf " <string>%s</string>\n" "${cmd[@]}"
|
||||
}
|
||||
|
||||
generate_mount_daemon() {
|
||||
local cmd_type="$1" # encrypted|unencrypted
|
||||
local volume_uuid="$2"
|
||||
cat <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>Label</key>
|
||||
<string>org.nixos.darwin-store</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
$(generate_mount_command "$cmd_type" "$volume_uuid")
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
}
|
||||
|
||||
_eat_bootout_err() {
|
||||
/usr/bin/grep -v "Boot-out failed: 36: Operation now in progress"
|
||||
}
|
||||
|
||||
# TODO: remove with --uninstall?
|
||||
uninstall_launch_daemon_directions() {
|
||||
local daemon_label="$1" # i.e., org.nixos.blah-blah
|
||||
local daemon_plist="$2" # abspath
|
||||
substep "Uninstall LaunchDaemon $daemon_label" \
|
||||
" sudo launchctl bootout system/$daemon_label" \
|
||||
" sudo rm $daemon_plist"
|
||||
}
|
||||
|
||||
uninstall_launch_daemon_prompt() {
|
||||
local daemon_label="$1" # i.e., org.nixos.blah-blah
|
||||
local daemon_plist="$2" # abspath
|
||||
local reason_for_daemon="$3"
|
||||
cat <<EOF
|
||||
|
||||
The installer adds a LaunchDaemon to $reason_for_daemon: $daemon_label
|
||||
EOF
|
||||
if ui_confirm "Can I remove it?"; then
|
||||
_sudo "to terminate the daemon" \
|
||||
launchctl bootout "system/$daemon_label" 2> >(_eat_bootout_err >&2) || true
|
||||
# this can "fail" with a message like:
|
||||
# Boot-out failed: 36: Operation now in progress
|
||||
_sudo "to remove the daemon definition" rm "$daemon_plist"
|
||||
fi
|
||||
}
|
||||
|
||||
nix_volume_mountd_uninstall_directions() {
|
||||
uninstall_launch_daemon_directions "org.nixos.darwin-store" \
|
||||
"$NIX_VOLUME_MOUNTD_DEST"
|
||||
}
|
||||
|
||||
nix_volume_mountd_uninstall_prompt() {
|
||||
uninstall_launch_daemon_prompt "org.nixos.darwin-store" \
|
||||
"$NIX_VOLUME_MOUNTD_DEST" \
|
||||
"mount your Nix volume"
|
||||
}
|
||||
|
||||
# TODO: move nix_daemon to install-darwin-multi-user if/when uninstall_launch_daemon_prompt moves up to install-multi-user
|
||||
nix_daemon_uninstall_prompt() {
|
||||
uninstall_launch_daemon_prompt "org.nixos.nix-daemon" \
|
||||
"$NIX_DAEMON_DEST" \
|
||||
"run the nix-daemon"
|
||||
}
|
||||
|
||||
# TODO: remove with --uninstall?
|
||||
nix_daemon_uninstall_directions() {
|
||||
uninstall_launch_daemon_directions "org.nixos.nix-daemon" \
|
||||
"$NIX_DAEMON_DEST"
|
||||
}
|
||||
|
||||
|
||||
# TODO: remove with --uninstall?
|
||||
synthetic_conf_uninstall_directions() {
|
||||
# :1 to strip leading slash
|
||||
substep "Remove ${NIX_ROOT:1} from /etc/synthetic.conf" \
|
||||
" If nix is the only entry: sudo rm /etc/synthetic.conf" \
|
||||
" Otherwise: sudo /usr/bin/sed -i '' -e '/^${NIX_ROOT:1}$/d' /etc/synthetic.conf"
|
||||
}
|
||||
|
||||
synthetic_conf_uninstall_prompt() {
|
||||
cat <<EOF
|
||||
|
||||
During install, I add '${NIX_ROOT:1}' to /etc/synthetic.conf, which instructs
|
||||
macOS to create an empty root directory for mounting the Nix volume.
|
||||
EOF
|
||||
# make the edit to a copy
|
||||
/usr/bin/grep -vE "^${NIX_ROOT:1}($|\t.{3,}$)" /etc/synthetic.conf > "$SCRATCH/synthetic.conf.edit"
|
||||
|
||||
if test_synthetic_conf_symlinked; then
|
||||
warning <<EOF
|
||||
|
||||
/etc/synthetic.conf already contains a line instructing your system
|
||||
to make '${NIX_ROOT}' as a symlink:
|
||||
$(/usr/bin/grep -nE "^${NIX_ROOT:1}\t.{3,}$" /etc/synthetic.conf)
|
||||
|
||||
This may mean your system has/had a non-standard Nix install.
|
||||
|
||||
The volume-creation process in this installer is *not* compatible
|
||||
with a symlinked store, so I'll have to remove this instruction to
|
||||
continue.
|
||||
|
||||
If you want/need to keep this instruction, answer 'n' to abort.
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
# ask to rm if this left the file empty aside from comments, else edit
|
||||
if /usr/bin/diff -q <(:) <(/usr/bin/grep -v "^#" "$SCRATCH/synthetic.conf.edit") &>/dev/null; then
|
||||
if confirm_rm "/etc/synthetic.conf"; then
|
||||
if test_nix_root_is_symlink; then
|
||||
failure >&2 <<EOF
|
||||
I removed /etc/synthetic.conf, but $NIX_ROOT is already a symlink
|
||||
(-> $(readlink "$NIX_ROOT")). The system should remove it when you reboot.
|
||||
Once you've rebooted, run the installer again.
|
||||
EOF
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
if confirm_edit "$SCRATCH/synthetic.conf.edit" "/etc/synthetic.conf"; then
|
||||
if test_nix_root_is_symlink; then
|
||||
failure >&2 <<EOF
|
||||
I edited Nix out of /etc/synthetic.conf, but $NIX_ROOT is already a symlink
|
||||
(-> $(readlink "$NIX_ROOT")). The system should remove it when you reboot.
|
||||
Once you've rebooted, run the installer again.
|
||||
EOF
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# fallback instructions
|
||||
echo "Manually remove nix from /etc/synthetic.conf"
|
||||
return 1
|
||||
}
|
||||
|
||||
add_nix_vol_fstab_line() {
|
||||
local uuid="$1"
|
||||
# shellcheck disable=SC1003,SC2026
|
||||
local escaped_mountpoint="${NIX_ROOT/ /'\\\'040}"
|
||||
shift
|
||||
EDITOR="/usr/bin/ex" _sudo "to add nix to fstab" "$@" <<EOF
|
||||
:a
|
||||
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners
|
||||
.
|
||||
:x
|
||||
EOF
|
||||
# TODO: preserving my notes on suid,owners above until resolved
|
||||
# There *may* be some issue regarding volume ownership, see nix#3156
|
||||
#
|
||||
# This isn't well-documented on its own; until it is,
|
||||
# let's keep track of knowledge/assumptions.
|
||||
# It seems like the cheapest fix is adding "suid,owners" to fstab, but:
|
||||
# - We don't have much info on this condition yet
|
||||
# - I'm not certain if these cause other problems?
|
||||
# - There's a "chown" component some people claim to need to fix this
|
||||
# that I don't understand yet
|
||||
# (Note however that I've had to add a chown step to handle
|
||||
# single->multi-user reinstalls, which may cover this)
|
||||
#
|
||||
# Warnings:
|
||||
# - Don't search "xart" if porn will cause you trouble :)
|
||||
# - Other xartutil flags do dangerous things. Don't run them
|
||||
# naively. If you must, search "xartutil" first.
|
||||
# I'm not sure if it's safe to approach this way?
|
||||
#
|
||||
# Assumptions:
|
||||
# - the "xART session seeds recovery utility"
|
||||
# appears to interact with xartstorageremoted
|
||||
# - `sudo xartutil --list` lists xART sessions
|
||||
# and their seeds and exits 0 if successful. If
|
||||
# not, it exits 1 and prints an error such as:
|
||||
# xartutil: ERROR: No supported link to the SEP present
|
||||
# - xART sessions/seeds are present when a T2 chip is
|
||||
# (and not, otherwise)
|
||||
# - the presence of a T2 chip means a newly-created
|
||||
# volume on the primary drive will be
|
||||
# encrypted at rest
|
||||
# - all together: `sudo xartutil --list`
|
||||
# should exit 0 if a new Nix Store volume will
|
||||
# be encrypted at rest, and exit 1 if not.
|
||||
sudo xartutil --list >/dev/null 2>/dev/null
|
||||
# I think I think the most-proper way to test for it is:
|
||||
# diskutil info -plist "$NIX_VOLUME_LABEL" | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1][name()='true']" -; echo $?
|
||||
#
|
||||
# There's also `sudo /usr/sbin/vsdbutil -c /path` (which is much faster, but is also
|
||||
# deprecated and needs minor parsing).
|
||||
#
|
||||
# If no one finds a problem with doing so, I think the simplest approach
|
||||
# is to just eagerly set this. I found a few imperative approaches:
|
||||
# (diskutil enableOwnership, ~100ms), a cheap one (/usr/sbin/vsdbutil -a, ~40-50ms),
|
||||
# a very cheap one (append the internal format to /var/db/volinfo.database).
|
||||
#
|
||||
# But vsdbutil's deprecation notice suggests using fstab, so I want to
|
||||
# give that a whirl first.
|
||||
#
|
||||
# TODO: when this is workable, poke infinisil about reproducing the issue
|
||||
# and confirming this fix?
|
||||
}
|
||||
|
||||
test_filevault_in_use() {
|
||||
fdesetup isactive >/dev/null
|
||||
delete_nix_vol_fstab_line() {
|
||||
# TODO: I'm scaffolding this to handle the new nix volumes
|
||||
# but it might be nice to generalize a smidge further to
|
||||
# go ahead and set up a pattern for curing "old" things
|
||||
# we no longer do?
|
||||
EDITOR="/usr/bin/patch" _sudo "to cut nix from fstab" "$@" < <(/usr/bin/diff /etc/fstab <(/usr/bin/grep -v "$NIX_ROOT apfs rw" /etc/fstab))
|
||||
# leaving some parts out of the grep; people may fiddle this a little?
|
||||
}
|
||||
|
||||
# use after error msg for conditions we don't understand
|
||||
suggest_report_error(){
|
||||
# ex "error: something sad happened :(" >&2
|
||||
echo " please report this @ https://github.com/nixos/nix/issues" >&2
|
||||
# TODO: hope to remove with --uninstall
|
||||
fstab_uninstall_directions() {
|
||||
substep "Remove ${NIX_ROOT} from /etc/fstab" \
|
||||
" If nix is the only entry: sudo rm /etc/fstab" \
|
||||
" Otherwise, run 'sudo /usr/sbin/vifs' to remove the nix line"
|
||||
}
|
||||
|
||||
fstab_uninstall_prompt() {
|
||||
cat <<EOF
|
||||
During install, I add '${NIX_ROOT}' to /etc/fstab so that macOS knows what
|
||||
mount options to use for the Nix volume.
|
||||
EOF
|
||||
cp /etc/fstab "$SCRATCH/fstab.edit"
|
||||
# technically doesn't need the _sudo path, but throwing away the
|
||||
# output is probably better than mostly-duplicating the code...
|
||||
delete_nix_vol_fstab_line patch "$SCRATCH/fstab.edit" &>/dev/null
|
||||
|
||||
# if the patch test edit, minus comment lines, is equal to empty (:)
|
||||
if /usr/bin/diff -q <(:) <(/usr/bin/grep -v "^#" "$SCRATCH/fstab.edit") &>/dev/null; then
|
||||
# this edit would leave it empty; propose deleting it
|
||||
if confirm_rm "/etc/fstab"; then
|
||||
return 0
|
||||
else
|
||||
echo "Remove nix from /etc/fstab (or remove the file)"
|
||||
fi
|
||||
else
|
||||
echo "I might be able to help you make this edit. Here's the diff:"
|
||||
if ! _diff "/etc/fstab" "$SCRATCH/fstab.edit" && ui_confirm "Does the change above look right?"; then
|
||||
delete_nix_vol_fstab_line /usr/sbin/vifs
|
||||
else
|
||||
echo "Remove nix from /etc/fstab (or remove the file)"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
remove_volume() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
_sudo "to unmount the Nix volume" \
|
||||
/usr/sbin/diskutil unmount force "$volume_special" || true # might not be mounted
|
||||
_sudo "to delete the Nix volume" \
|
||||
/usr/sbin/diskutil apfs deleteVolume "$volume_special"
|
||||
}
|
||||
|
||||
# aspiration: robust enough to both fix problems
|
||||
# *and* update older darwin volumes
|
||||
cure_volume() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
local volume_uuid="$2"
|
||||
header "Found existing Nix volume"
|
||||
row " special" "$volume_special"
|
||||
row " uuid" "$volume_uuid"
|
||||
|
||||
if volume_encrypted "$volume_special"; then
|
||||
row "encrypted" "yes"
|
||||
if volume_pass_works "$volume_special" "$volume_uuid"; then
|
||||
NIX_VOLUME_DO_ENCRYPT=0
|
||||
ok "Found a working decryption password in keychain :)"
|
||||
echo ""
|
||||
else
|
||||
# - this is a volume we made, and
|
||||
# - the user encrypted it on their own
|
||||
# - something deleted the credential
|
||||
# - this is an old or BYO volume and the pw
|
||||
# just isn't somewhere we can find it.
|
||||
#
|
||||
# We're going to explain why we're freaking out
|
||||
# and prompt them to either delete the volume
|
||||
# (requiring a sudo auth), or abort to fix
|
||||
warning <<EOF
|
||||
|
||||
This volume is encrypted, but I don't see a password to decrypt it.
|
||||
The quick fix is to let me delete this volume and make you a new one.
|
||||
If that's okay, enter your (sudo) password to continue. If not, you
|
||||
can ensure the decryption password is in your system keychain with a
|
||||
"Where" (service) field set to this volume's UUID:
|
||||
$volume_uuid
|
||||
EOF
|
||||
if password_confirm "delete this volume"; then
|
||||
remove_volume "$volume_special"
|
||||
else
|
||||
# TODO: this is a good design case for a warn-and
|
||||
# remind idiom...
|
||||
failure <<EOF
|
||||
Your Nix volume is encrypted, but I couldn't find its password. Either:
|
||||
- Delete or rename the volume out of the way
|
||||
- Ensure its decryption password is in the system keychain with a
|
||||
"Where" (service) field set to this volume's UUID:
|
||||
$volume_uuid
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
elif test_filevault_in_use; then
|
||||
row "encrypted" "no"
|
||||
warning <<EOF
|
||||
FileVault is on, but your $NIX_VOLUME_LABEL volume isn't encrypted.
|
||||
EOF
|
||||
# if we're interactive, give them a chance to
|
||||
# encrypt the volume. If not, /shrug
|
||||
if ! headless && (( NIX_VOLUME_DO_ENCRYPT == 1 )); then
|
||||
if ui_confirm "Should I encrypt it and add the decryption key to your keychain?"; then
|
||||
encrypt_volume "$volume_uuid" "$NIX_VOLUME_LABEL"
|
||||
NIX_VOLUME_DO_ENCRYPT=0
|
||||
else
|
||||
NIX_VOLUME_DO_ENCRYPT=0
|
||||
reminder "FileVault is on, but your $NIX_VOLUME_LABEL volume isn't encrypted."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
row "encrypted" "no"
|
||||
fi
|
||||
}
|
||||
|
||||
remove_volume_artifacts() {
|
||||
if test_synthetic_conf_either; then
|
||||
# NIX_ROOT is in synthetic.conf
|
||||
if synthetic_conf_uninstall_prompt; then
|
||||
# TODO: moot until we tackle uninstall, but when we're
|
||||
# actually uninstalling, we should issue:
|
||||
# reminder "macOS will clean up the empty mount-point directory at $NIX_ROOT on reboot."
|
||||
:
|
||||
fi
|
||||
fi
|
||||
if test_fstab; then
|
||||
fstab_uninstall_prompt
|
||||
fi
|
||||
|
||||
if test_nix_volume_mountd_installed; then
|
||||
nix_volume_mountd_uninstall_prompt
|
||||
fi
|
||||
}
|
||||
|
||||
setup_synthetic_conf() {
|
||||
if test_nix_root_is_symlink; then
|
||||
if ! test_synthetic_conf_symlinked; then
|
||||
failure >&2 <<EOF
|
||||
error: $NIX_ROOT is a symlink (-> $(readlink "$NIX_ROOT")).
|
||||
Please remove it. If nix is in /etc/synthetic.conf, remove it and reboot.
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
if ! test_synthetic_conf_mountable; then
|
||||
task "Configuring /etc/synthetic.conf to make a mount-point at $NIX_ROOT" >&2
|
||||
# technically /etc/synthetic.d/nix is supported in Big Sur+
|
||||
# but handling both takes even more code...
|
||||
_sudo "to add Nix to /etc/synthetic.conf" \
|
||||
/usr/bin/ex /etc/synthetic.conf <<EOF
|
||||
:a
|
||||
${NIX_ROOT:1}
|
||||
.
|
||||
:x
|
||||
EOF
|
||||
if ! test_synthetic_conf_mountable; then
|
||||
failure "error: failed to configure synthetic.conf" >&2
|
||||
fi
|
||||
create_synthetic_objects
|
||||
if ! test_nix; then
|
||||
failure >&2 <<EOF
|
||||
error: failed to bootstrap $NIX_ROOT
|
||||
If you enabled FileVault after booting, this is likely a known issue
|
||||
with macOS that you'll have to reboot to fix. If you didn't enable FV,
|
||||
though, please open an issue describing how the system that you see
|
||||
this error on was set up.
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
setup_fstab() {
|
||||
local volume_uuid="$1"
|
||||
# fstab used to be responsible for mounting the volume. Now the last
|
||||
# step adds a LaunchDaemon responsible for mounting. This is technically
|
||||
# redundant for mounting, but diskutil appears to pick up mount options
|
||||
# from fstab (and diskutil's support for specifying them directly is not
|
||||
# consistent across versions/subcommands).
|
||||
if ! test_fstab; then
|
||||
task "Configuring /etc/fstab to specify volume mount options" >&2
|
||||
add_nix_vol_fstab_line "$volume_uuid" /usr/sbin/vifs
|
||||
fi
|
||||
}
|
||||
|
||||
encrypt_volume() {
|
||||
local volume_uuid="$1"
|
||||
local volume_label="$2"
|
||||
local password
|
||||
# Note: mount/unmount are late additions to support the right order
|
||||
# of operations for creating the volume and then baking its uuid into
|
||||
# other artifacts; not as well-trod wrt to potential errors, race
|
||||
# conditions, etc.
|
||||
|
||||
/usr/sbin/diskutil mount "$volume_label"
|
||||
|
||||
password="$(/usr/bin/xxd -l 32 -p -c 256 /dev/random)"
|
||||
_sudo "to add your Nix volume's password to Keychain" \
|
||||
/usr/bin/security -i <<EOF
|
||||
add-generic-password -a "$volume_label" -s "$volume_uuid" -l "$volume_label encryption password" -D "Encrypted volume password" -j "Added automatically by the Nix installer for use by $NIX_VOLUME_MOUNTD_DEST" -w "$password" -T /System/Library/CoreServices/APFSUserAgent -T /System/Library/CoreServices/CSUserAgent -T /usr/bin/security "/Library/Keychains/System.keychain"
|
||||
EOF
|
||||
builtin printf "%s" "$password" | _sudo "to encrypt your Nix volume" \
|
||||
/usr/sbin/diskutil apfs encryptVolume "$volume_label" -user disk -stdinpassphrase
|
||||
|
||||
/usr/sbin/diskutil unmount force "$volume_label"
|
||||
}
|
||||
|
||||
create_volume() {
|
||||
# Notes:
|
||||
# 1) using `-nomount` instead of `-mountpoint "$NIX_ROOT"` to get
|
||||
# its UUID and set mount opts in fstab before first mount
|
||||
#
|
||||
# 2) system is in some sense less secure than user keychain... (it's
|
||||
# possible to read the password for decrypting the keychain) but
|
||||
# the user keychain appears to be available too late. As far as I
|
||||
# can tell, the file with this password (/var/db/SystemKey) is
|
||||
# inside the FileVault envelope. If that isn't true, it may make
|
||||
# sense to store the password inside the envelope?
|
||||
#
|
||||
# 3) At some point it would be ideal to have a small binary to serve
|
||||
# as the daemon itself, and for it to replace /usr/bin/security here.
|
||||
#
|
||||
# 4) *UserAgent exemptions should let the system seamlessly supply the
|
||||
# password if noauto is removed from fstab entry. This is intentional;
|
||||
# the user will hopefully look for help if the volume stops mounting,
|
||||
# rather than failing over into subtle race-condition problems.
|
||||
#
|
||||
# 5) If we ever get users griping about not having space to do
|
||||
# anything useful with Nix, it is possibly to specify
|
||||
# `-reserve 10g` or something, which will fail w/o that much
|
||||
#
|
||||
# 6) getting special w/ awk may be fragile, but doing it to:
|
||||
# - save time over running slow diskutil commands
|
||||
# - skirt risk we grab wrong volume if multiple match
|
||||
/usr/sbin/diskutil apfs addVolume "$NIX_VOLUME_USE_DISK" "$NIX_VOLUME_FS" "$NIX_VOLUME_LABEL" -nomount | /usr/bin/awk '/Created new APFS Volume/ {print $5}'
|
||||
}
|
||||
|
||||
volume_uuid_from_special() {
|
||||
local volume_special="$1" # (i.e., disk1s7)
|
||||
# For reasons I won't pretend to fathom, this returns 253 when it works
|
||||
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -k "$volume_special" || true
|
||||
}
|
||||
|
||||
# this sometimes clears immediately, and AFAIK clears
|
||||
# within about 1s. diskutil info on an unmounted path
|
||||
# fails in around 50-100ms and a match takes about
|
||||
# 250-300ms. I suspect it's usually ~250-750ms
|
||||
await_volume() {
|
||||
# caution: this could, in theory, get stuck
|
||||
until /usr/sbin/diskutil info "$NIX_ROOT" &>/dev/null; do
|
||||
:
|
||||
done
|
||||
}
|
||||
|
||||
setup_volume() {
|
||||
local use_special use_uuid profile_packages
|
||||
task "Creating a Nix volume" >&2
|
||||
# DOING: I'm tempted to wrap this call in a grep to get the new disk special without doing anything too complex, but this sudo wrapper *is* a little complex, so it'll be a PITA unless maybe we can skip sudo on this. Let's just try it without.
|
||||
|
||||
use_special="${NIX_VOLUME_USE_SPECIAL:-$(create_volume)}"
|
||||
|
||||
use_uuid=${NIX_VOLUME_USE_UUID:-$(volume_uuid_from_special "$use_special")}
|
||||
|
||||
setup_fstab "$use_uuid"
|
||||
|
||||
if should_encrypt_volume; then
|
||||
encrypt_volume "$use_uuid" "$NIX_VOLUME_LABEL"
|
||||
setup_volume_daemon "encrypted" "$use_uuid"
|
||||
# TODO: might be able to save ~60ms by caching or setting
|
||||
# this somewhere rather than re-checking here.
|
||||
elif volume_encrypted "$use_special"; then
|
||||
setup_volume_daemon "encrypted" "$use_uuid"
|
||||
else
|
||||
setup_volume_daemon "unencrypted" "$use_uuid"
|
||||
fi
|
||||
|
||||
await_volume
|
||||
|
||||
# TODO: below is a vague kludge for now; I just don't know
|
||||
# what if any safe action there is to take here. Also, the
|
||||
# reminder isn't very helpful.
|
||||
# I'm less sure where this belongs, but it also wants mounted, pre-install
|
||||
if type -p nix-env; then
|
||||
profile_packages="$(nix-env --query --installed)"
|
||||
# TODO: can probably do below faster w/ read
|
||||
# intentionally unquoted string to eat whitespace in wc output
|
||||
# shellcheck disable=SC2046,SC2059
|
||||
if ! [ $(printf "$profile_packages" | /usr/bin/wc -l) = "0" ]; then
|
||||
reminder <<EOF
|
||||
Nix now supports only multi-user installs on Darwin/macOS, and your user's
|
||||
Nix profile has some packages in it. These packages may obscure those in the
|
||||
default profile, including the Nix this installer will add. You should
|
||||
review these packages:
|
||||
$profile_packages
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
setup_volume_daemon() {
|
||||
local cmd_type="$1" # encrypted|unencrypted
|
||||
local volume_uuid="$2"
|
||||
if ! test_voldaemon; then
|
||||
task "Configuring LaunchDaemon to mount '$NIX_VOLUME_LABEL'" >&2
|
||||
_sudo "to install the Nix volume mounter" /usr/bin/ex "$NIX_VOLUME_MOUNTD_DEST" <<EOF
|
||||
:a
|
||||
$(generate_mount_daemon "$cmd_type" "$volume_uuid")
|
||||
.
|
||||
:x
|
||||
EOF
|
||||
|
||||
# TODO: should probably alert the user if this is disabled?
|
||||
_sudo "to launch the Nix volume mounter" \
|
||||
launchctl bootstrap system "$NIX_VOLUME_MOUNTD_DEST" || true
|
||||
# TODO: confirm whether kickstart is necessesary?
|
||||
# I feel a little superstitous, but it can guard
|
||||
# against multiple problems (doesn't start, old
|
||||
# version still running for some reason...)
|
||||
_sudo "to launch the Nix volume mounter" \
|
||||
launchctl kickstart -k system/org.nixos.darwin-store
|
||||
fi
|
||||
}
|
||||
|
||||
setup_darwin_volume() {
|
||||
setup_synthetic_conf
|
||||
setup_volume
|
||||
}
|
||||
|
||||
if [ "$_CREATE_VOLUME_NO_MAIN" = 1 ]; then
|
||||
if [ -n "$*" ]; then
|
||||
"$@" # expose functions in case we want multiple routines?
|
||||
fi
|
||||
else
|
||||
# no reason to pay for bash to process this
|
||||
main() {
|
||||
(
|
||||
{
|
||||
echo ""
|
||||
echo " ------------------------------------------------------------------ "
|
||||
echo " | This installer will create a volume for the nix store and |"
|
||||
echo " | configure it to mount at /nix. Follow these steps to uninstall. |"
|
||||
echo " | configure it to mount at $NIX_ROOT. Follow these steps to uninstall. |"
|
||||
echo " ------------------------------------------------------------------ "
|
||||
echo ""
|
||||
echo " 1. Remove the entry from fstab using 'sudo vifs'"
|
||||
echo " 2. Destroy the data volume using 'diskutil apfs deleteVolume'"
|
||||
echo " 3. Remove the 'nix' line from /etc/synthetic.conf or the file"
|
||||
echo " 1. Remove the entry from fstab using 'sudo /usr/sbin/vifs'"
|
||||
echo " 2. Run 'sudo launchctl bootout system/org.nixos.darwin-store'"
|
||||
echo " 3. Remove $NIX_VOLUME_MOUNTD_DEST"
|
||||
echo " 4. Destroy the data volume using '/usr/sbin/diskutil apfs deleteVolume'"
|
||||
echo " 5. Remove the 'nix' line from /etc/synthetic.conf (or the file)"
|
||||
echo ""
|
||||
) >&2
|
||||
} >&2
|
||||
|
||||
if test_nix_symlink; then
|
||||
echo "error: /nix is a symlink, please remove it and make sure it's not in synthetic.conf (in which case a reboot is required)" >&2
|
||||
echo " /nix -> $(readlink "/nix")" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! test_synthetic_conf; then
|
||||
echo "Configuring /etc/synthetic.conf..." >&2
|
||||
echo nix | sudo tee -a /etc/synthetic.conf
|
||||
if ! test_synthetic_conf; then
|
||||
echo "error: failed to configure synthetic.conf;" >&2
|
||||
suggest_report_error
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test_nix; then
|
||||
echo "Creating mountpoint for /nix..." >&2
|
||||
create_synthetic_objects # the ones we defined in synthetic.conf
|
||||
if ! test_nix; then
|
||||
sudo mkdir -p /nix 2>/dev/null || true
|
||||
fi
|
||||
if ! test_nix; then
|
||||
echo "error: failed to bootstrap /nix; if a reboot doesn't help," >&2
|
||||
suggest_report_error
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
disk="$(root_disk_identifier)"
|
||||
volume=$(find_nix_volume "$disk")
|
||||
if [ -z "$volume" ]; then
|
||||
echo "Creating a Nix Store volume..." >&2
|
||||
|
||||
if test_filevault_in_use; then
|
||||
# TODO: Not sure if it's in-scope now, but `diskutil apfs list`
|
||||
# shows both filevault and encrypted at rest status, and it
|
||||
# may be the more semantic way to test for this? It'll show
|
||||
# `FileVault: No (Encrypted at rest)`
|
||||
# `FileVault: No`
|
||||
# `FileVault: Yes (Unlocked)`
|
||||
# and so on.
|
||||
if test_t2_chip_present; then
|
||||
echo "warning: boot volume is FileVault-encrypted, but the Nix store volume" >&2
|
||||
echo " is only encrypted at rest." >&2
|
||||
echo " See https://nixos.org/nix/manual/#sect-macos-installation" >&2
|
||||
else
|
||||
echo "error: refusing to create Nix store volume because the boot volume is" >&2
|
||||
echo " FileVault encrypted, but encryption-at-rest is not available." >&2
|
||||
echo " Manually create a volume for the store and re-run this script." >&2
|
||||
echo " See https://nixos.org/nix/manual/#sect-macos-installation" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
sudo diskutil apfs addVolume "$disk" APFS 'Nix Store' -mountpoint /nix
|
||||
volume="Nix Store"
|
||||
else
|
||||
echo "Using existing '$volume' volume" >&2
|
||||
fi
|
||||
|
||||
if ! test_fstab; then
|
||||
echo "Configuring /etc/fstab..." >&2
|
||||
label=$(echo "$volume" | sed 's/ /\\040/g')
|
||||
# shellcheck disable=SC2209
|
||||
printf "\$a\nLABEL=%s /nix apfs rw,nobrowse\n.\nwq\n" "$label" | EDITOR=ed sudo vifs
|
||||
fi
|
||||
setup_darwin_volume
|
||||
}
|
||||
|
||||
main "$@"
|
||||
fi
|
||||
|
|
|
@ -3,57 +3,99 @@
|
|||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
readonly PLIST_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
# create by default; set 0 to DIY, use a symlink, etc.
|
||||
readonly NIX_VOLUME_CREATE=${NIX_VOLUME_CREATE:-1} # now default
|
||||
NIX_FIRST_BUILD_UID="301"
|
||||
NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
|
||||
|
||||
# caution: may update times on / if not run as normal non-root user
|
||||
read_only_root() {
|
||||
# this touch command ~should~ always produce an error
|
||||
# as of this change I confirmed /usr/bin/touch emits:
|
||||
# "touch: /: Read-only file system" Catalina+ and Big Sur
|
||||
# "touch: /: Permission denied" Mojave
|
||||
# (not matching prefix for compat w/ coreutils touch in case using
|
||||
# an explicit path causes problems; its prefix differs)
|
||||
[[ "$(/usr/bin/touch / 2>&1)" = *"Read-only file system" ]]
|
||||
|
||||
# Avoiding the slow semantic way to get this information (~330ms vs ~8ms)
|
||||
# unless using touch causes problems. Just in case, that approach is:
|
||||
# diskutil info -plist / | <find the Writable or WritableVolume keys>, i.e.
|
||||
# diskutil info -plist / | xmllint --xpath "name(/plist/dict/key[text()='Writable']/following-sibling::*[1])" -
|
||||
}
|
||||
|
||||
if read_only_root && [ "$NIX_VOLUME_CREATE" = 1 ]; then
|
||||
should_create_volume() { return 0; }
|
||||
else
|
||||
should_create_volume() { return 1; }
|
||||
fi
|
||||
|
||||
# shellcheck source=./create-darwin-volume.sh
|
||||
. "$EXTRACTED_NIX_PATH/create-darwin-volume.sh" "no-main"
|
||||
|
||||
dsclattr() {
|
||||
/usr/bin/dscl . -read "$1" \
|
||||
| awk "/$2/ { print \$2 }"
|
||||
| /usr/bin/awk "/$2/ { print \$2 }"
|
||||
}
|
||||
|
||||
poly_validate_assumptions() {
|
||||
if [ "$(uname -s)" != "Darwin" ]; then
|
||||
failure "This script is for use with macOS!"
|
||||
test_nix_daemon_installed() {
|
||||
test -e "$NIX_DAEMON_DEST"
|
||||
}
|
||||
|
||||
poly_cure_artifacts() {
|
||||
if should_create_volume; then
|
||||
task "Fixing any leftover Nix volume state"
|
||||
cat <<EOF
|
||||
Before I try to install, I'll check for any existing Nix volume config
|
||||
and ask for your permission to remove it (so that the installer can
|
||||
start fresh). I'll also ask for permission to fix any issues I spot.
|
||||
EOF
|
||||
cure_volumes
|
||||
remove_volume_artifacts
|
||||
fi
|
||||
}
|
||||
|
||||
poly_service_installed_check() {
|
||||
[ -e "$PLIST_DEST" ]
|
||||
if should_create_volume; then
|
||||
test_nix_daemon_installed || test_nix_volume_mountd_installed
|
||||
else
|
||||
test_nix_daemon_installed
|
||||
fi
|
||||
}
|
||||
|
||||
poly_service_uninstall_directions() {
|
||||
cat <<EOF
|
||||
$1. Delete $PLIST_DEST
|
||||
|
||||
sudo launchctl unload $PLIST_DEST
|
||||
sudo rm $PLIST_DEST
|
||||
|
||||
EOF
|
||||
echo "$1. Remove macOS-specific components:"
|
||||
if should_create_volume && test_nix_volume_mountd_installed; then
|
||||
darwin_volume_uninstall_directions
|
||||
fi
|
||||
if test_nix_daemon_installed; then
|
||||
nix_daemon_uninstall_directions
|
||||
fi
|
||||
}
|
||||
|
||||
poly_service_setup_note() {
|
||||
cat <<EOF
|
||||
- load and start a LaunchDaemon (at $PLIST_DEST) for nix-daemon
|
||||
|
||||
EOF
|
||||
if should_create_volume; then
|
||||
echo " - create a Nix volume and a LaunchDaemon to mount it"
|
||||
fi
|
||||
echo " - create a LaunchDaemon (at $NIX_DAEMON_DEST) for nix-daemon"
|
||||
echo ""
|
||||
}
|
||||
|
||||
poly_extra_try_me_commands() {
|
||||
:
|
||||
}
|
||||
poly_extra_setup_instructions(){
|
||||
:
|
||||
}
|
||||
|
||||
poly_configure_nix_daemon_service() {
|
||||
task "Setting up the nix-daemon LaunchDaemon"
|
||||
_sudo "to set up the nix-daemon as a LaunchDaemon" \
|
||||
cp -f "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST"
|
||||
/bin/cp -f "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST"
|
||||
|
||||
_sudo "to load the LaunchDaemon plist for nix-daemon" \
|
||||
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
|
||||
_sudo "to start the nix-daemon" \
|
||||
launchctl start org.nixos.nix-daemon
|
||||
|
||||
launchctl kickstart -k system/org.nixos.nix-daemon
|
||||
}
|
||||
|
||||
poly_group_exists() {
|
||||
|
@ -94,6 +136,8 @@ poly_user_home_get() {
|
|||
}
|
||||
|
||||
poly_user_home_set() {
|
||||
# This can trigger a permission prompt now:
|
||||
# "Terminal" would like to administer your computer. Administration can include modifying passwords, networking, and system settings.
|
||||
_sudo "in order to give $1 a safe home directory" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2"
|
||||
}
|
||||
|
@ -119,7 +163,7 @@ poly_user_shell_set() {
|
|||
poly_user_in_group_check() {
|
||||
username=$1
|
||||
group=$2
|
||||
dseditgroup -o checkmember -m "$username" "$group" > /dev/null 2>&1
|
||||
/usr/sbin/dseditgroup -o checkmember -m "$username" "$group" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
poly_user_in_group_set() {
|
||||
|
@ -149,3 +193,17 @@ poly_create_build_user() {
|
|||
/usr/bin/dscl . create "/Users/$username" \
|
||||
UniqueID "${uid}"
|
||||
}
|
||||
|
||||
poly_prepare_to_install() {
|
||||
if should_create_volume; then
|
||||
header "Preparing a Nix volume"
|
||||
# intentional indent below to match task indent
|
||||
cat <<EOF
|
||||
Nix traditionally stores its data in the root directory $NIX_ROOT, but
|
||||
macOS now (starting in 10.15 Catalina) has a read-only root directory.
|
||||
To support Nix, I will create a volume and configure macOS to mount it
|
||||
at $NIX_ROOT.
|
||||
EOF
|
||||
setup_darwin_volume
|
||||
fi
|
||||
}
|
||||
|
|
|
@ -25,13 +25,15 @@ readonly RED='\033[31m'
|
|||
readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32}
|
||||
readonly NIX_BUILD_GROUP_ID="30000"
|
||||
readonly NIX_BUILD_GROUP_NAME="nixbld"
|
||||
readonly NIX_FIRST_BUILD_UID="30001"
|
||||
# darwin installer needs to override these
|
||||
NIX_FIRST_BUILD_UID="30001"
|
||||
NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
|
||||
# Please don't change this. We don't support it, because the
|
||||
# default shell profile that comes with Nix doesn't support it.
|
||||
readonly NIX_ROOT="/nix"
|
||||
readonly NIX_EXTRA_CONF=${NIX_EXTRA_CONF:-}
|
||||
|
||||
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshenv")
|
||||
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshenv" "/etc/bash.bashrc" "/etc/zsh/zshenv")
|
||||
readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
|
||||
readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
|
||||
|
||||
|
@ -41,7 +43,7 @@ readonly NIX_INSTALLED_CACERT="@cacert@"
|
|||
#readonly NIX_INSTALLED_CACERT="/nix/store/7dxhzymvy330i28ii676fl1pqwcahv2f-nss-cacert-3.49.2"
|
||||
readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
|
||||
|
||||
readonly ROOT_HOME=$(echo ~root)
|
||||
readonly ROOT_HOME=~root
|
||||
|
||||
if [ -t 0 ]; then
|
||||
readonly IS_HEADLESS='no'
|
||||
|
@ -57,14 +59,19 @@ headless() {
|
|||
fi
|
||||
}
|
||||
|
||||
contactme() {
|
||||
contact_us() {
|
||||
echo "You can open an issue at https://github.com/nixos/nix/issues"
|
||||
echo ""
|
||||
echo "Or feel free to contact the team:"
|
||||
echo " - Matrix: #nix:nixos.org"
|
||||
echo " - IRC: in #nixos on irc.libera.chat"
|
||||
echo " - twitter: @nixos_org"
|
||||
echo " - forum: https://discourse.nixos.org"
|
||||
}
|
||||
get_help() {
|
||||
echo "We'd love to help if you need it."
|
||||
echo ""
|
||||
echo "If you can, open an issue at https://github.com/nixos/nix/issues"
|
||||
echo ""
|
||||
echo "Or feel free to contact the team,"
|
||||
echo " - on IRC #nixos on irc.freenode.net"
|
||||
echo " - on twitter @nixos_org"
|
||||
contact_us
|
||||
}
|
||||
|
||||
uninstall_directions() {
|
||||
|
@ -100,11 +107,10 @@ $step. Delete the files Nix added to your system:
|
|||
and that is it.
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
nix_user_for_core() {
|
||||
printf "nixbld%d" "$1"
|
||||
printf "$NIX_BUILD_USER_NAME_TEMPLATE" "$1"
|
||||
}
|
||||
|
||||
nix_uid_for_core() {
|
||||
|
@ -168,7 +174,7 @@ failure() {
|
|||
header "oh no!"
|
||||
_textout "$RED" "$@"
|
||||
echo ""
|
||||
_textout "$RED" "$(contactme)"
|
||||
_textout "$RED" "$(get_help)"
|
||||
trap finish_cleanup EXIT
|
||||
exit 1
|
||||
}
|
||||
|
@ -199,6 +205,95 @@ ui_confirm() {
|
|||
return 1
|
||||
}
|
||||
|
||||
printf -v _UNCHANGED_GRP_FMT "%b" $'\033[2m%='"$ESC" # "dim"
|
||||
# bold+invert+red and bold+invert+green just for the +/- below
|
||||
# red/green foreground for rest of the line
|
||||
printf -v _OLD_LINE_FMT "%b" $'\033[1;7;31m-'"$ESC ${RED}%L${ESC}"
|
||||
printf -v _NEW_LINE_FMT "%b" $'\033[1;7;32m+'"$ESC ${GREEN}%L${ESC}"
|
||||
|
||||
_diff() {
|
||||
# simple colorized diff comatible w/ pre `--color` versions
|
||||
diff --unchanged-group-format="$_UNCHANGED_GRP_FMT" --old-line-format="$_OLD_LINE_FMT" --new-line-format="$_NEW_LINE_FMT" --unchanged-line-format=" %L" "$@"
|
||||
}
|
||||
|
||||
confirm_rm() {
|
||||
local path="$1"
|
||||
if ui_confirm "Can I remove $path?"; then
|
||||
_sudo "to remove $path" rm "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
confirm_edit() {
|
||||
local path="$1"
|
||||
local edit_path="$2"
|
||||
cat <<EOF
|
||||
|
||||
Nix isn't the only thing in $path,
|
||||
but I think I know how to edit it out.
|
||||
Here's the diff:
|
||||
EOF
|
||||
|
||||
# could technically test the diff, but caller should do it
|
||||
_diff "$path" "$edit_path"
|
||||
if ui_confirm "Does the change above look right?"; then
|
||||
_sudo "remove nix from $path" cp "$edit_path" "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
_SERIOUS_BUSINESS="${RED}%s:${ESC} "
|
||||
password_confirm() {
|
||||
local do_something_consequential="$1"
|
||||
if ui_confirm "Can I $do_something_consequential?"; then
|
||||
# shellcheck disable=SC2059
|
||||
sudo -kv --prompt="$(printf "${_SERIOUS_BUSINESS}" "Enter your password to $do_something_consequential")"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Support accumulating reminders over the course of a run and showing
|
||||
# them at the end. An example where this helps: the installer changes
|
||||
# something, but it won't work without a reboot. If you tell the user
|
||||
# when you do it, they may miss it in the stream. The value of the
|
||||
# setting isn't enough to decide whether to message because you only
|
||||
# need to message if you *changed* it.
|
||||
|
||||
# reminders stored in array delimited by empty entry; if ! headless,
|
||||
# user is asked to confirm after each delimiter.
|
||||
_reminders=()
|
||||
((_remind_num=1))
|
||||
|
||||
remind() {
|
||||
# (( arithmetic expression ))
|
||||
if (( _remind_num > 1 )); then
|
||||
header "Reminders"
|
||||
for line in "${_reminders[@]}"; do
|
||||
echo "$line"
|
||||
if ! headless && [ "${#line}" = 0 ]; then
|
||||
if read -r -p "Press enter/return to acknowledge."; then
|
||||
printf $'\033[A\33[2K\r'
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
reminder() {
|
||||
printf -v label "${BLUE}[ %d ]${ESC}" "$_remind_num"
|
||||
_reminders+=("$label")
|
||||
if [[ "$*" = "" ]]; then
|
||||
while read -r line; do
|
||||
_reminders+=("$line")
|
||||
done
|
||||
else
|
||||
# this expands each arg to an array entry (and each entry will
|
||||
# ultimately be a separate line in the output)
|
||||
_reminders+=("$@")
|
||||
fi
|
||||
_reminders+=("")
|
||||
((_remind_num++))
|
||||
}
|
||||
|
||||
__sudo() {
|
||||
local expl="$1"
|
||||
local cmd="$2"
|
||||
|
@ -219,18 +314,18 @@ _sudo() {
|
|||
local expl="$1"
|
||||
shift
|
||||
if ! headless; then
|
||||
__sudo "$expl" "$*"
|
||||
__sudo "$expl" "$*" >&2
|
||||
fi
|
||||
sudo "$@"
|
||||
}
|
||||
|
||||
|
||||
readonly SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX)
|
||||
function finish_cleanup {
|
||||
readonly SCRATCH=$(mktemp -d "${TMPDIR:-/tmp/}tmp.XXXXXXXXXX")
|
||||
finish_cleanup() {
|
||||
rm -rf "$SCRATCH"
|
||||
}
|
||||
|
||||
function finish_fail {
|
||||
finish_fail() {
|
||||
finish_cleanup
|
||||
|
||||
failure <<EOF
|
||||
|
@ -242,45 +337,46 @@ EOF
|
|||
}
|
||||
trap finish_fail EXIT
|
||||
|
||||
channel_update_failed=0
|
||||
function finish_success {
|
||||
finish_cleanup
|
||||
|
||||
finish_success() {
|
||||
ok "Alright! We're done!"
|
||||
if [ "x$channel_update_failed" = x1 ]; then
|
||||
echo ""
|
||||
echo "But fetching the nixpkgs channel failed. (Are you offline?)"
|
||||
echo "To try again later, run \"sudo -i nix-channel --update nixpkgs\"."
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
Before Nix will work in your existing shells, you'll need to close
|
||||
them and open them again. Other than that, you should be ready to go.
|
||||
|
||||
Try it! Open a new terminal, and type:
|
||||
$(poly_extra_try_me_commands)
|
||||
$ nix-shell -p nix-info --run "nix-info -m"
|
||||
$(poly_extra_setup_instructions)
|
||||
Thank you for using this installer. If you have any feedback, don't
|
||||
hesitate:
|
||||
|
||||
$(contactme)
|
||||
Thank you for using this installer. If you have any feedback or need
|
||||
help, don't hesitate:
|
||||
|
||||
$(contact_us)
|
||||
EOF
|
||||
|
||||
remind
|
||||
finish_cleanup
|
||||
}
|
||||
|
||||
finish_uninstall_success() {
|
||||
ok "Alright! Nix should be removed!"
|
||||
|
||||
cat <<EOF
|
||||
If you spot anything this uninstaller missed or have feedback,
|
||||
don't hesitate:
|
||||
|
||||
$(contact_us)
|
||||
EOF
|
||||
remind
|
||||
finish_cleanup
|
||||
}
|
||||
|
||||
remove_nix_artifacts() {
|
||||
failure "Not implemented yet"
|
||||
}
|
||||
|
||||
cure_artifacts() {
|
||||
poly_cure_artifacts
|
||||
# remove_nix_artifacts (LATER)
|
||||
}
|
||||
|
||||
validate_starting_assumptions() {
|
||||
poly_validate_assumptions
|
||||
|
||||
if [ $EUID -eq 0 ]; then
|
||||
failure <<EOF
|
||||
Please do not run this script with root privileges. We will call sudo
|
||||
when we need to.
|
||||
EOF
|
||||
fi
|
||||
|
||||
if type nix-env 2> /dev/null >&2; then
|
||||
warning <<EOF
|
||||
Nix already appears to be installed. This installer may run into issues.
|
||||
|
@ -442,18 +538,46 @@ create_build_users() {
|
|||
|
||||
create_directories() {
|
||||
# FIXME: remove all of this because it duplicates LocalStore::LocalStore().
|
||||
task "Setting up the basic directory structure"
|
||||
if [ -d "$NIX_ROOT" ]; then
|
||||
# if /nix already exists, take ownership
|
||||
#
|
||||
# Caution: notes below are macOS-y
|
||||
# This is a bit of a goldilocks zone for taking ownership
|
||||
# if there are already files on the volume; the volume is
|
||||
# now mounted, but we haven't added a bunch of new files
|
||||
|
||||
# this is probably a bit slow; I've been seeing 3.3-4s even
|
||||
# when promptly installed over a fresh single-user install.
|
||||
# In case anyone's aware of a shortcut.
|
||||
# `|| true`: .Trashes errors w/o full disk perm
|
||||
|
||||
# rumor per #4488 that macOS 11.2 may not have
|
||||
# sbin on path, and that's where chown is, but
|
||||
# since this bit is cross-platform:
|
||||
# - first try with `command -vp` to try and find
|
||||
# chown in the usual places
|
||||
# - fall back on `command -v` which would find
|
||||
# any chown on path
|
||||
# if we don't find one, the command is already
|
||||
# hiding behind || true, and the general state
|
||||
# should be one the user can repair once they
|
||||
# figure out where chown is...
|
||||
local get_chr_own="$(command -vp chown)"
|
||||
if [[ -z "$get_chr_own" ]]; then
|
||||
get_chr_own="$(command -v chown)"
|
||||
fi
|
||||
_sudo "to take root ownership of existing Nix store files" \
|
||||
"$get_chr_own" -R "root:$NIX_BUILD_GROUP_NAME" "$NIX_ROOT" || true
|
||||
fi
|
||||
_sudo "to make the basic directory structure of Nix (part 1)" \
|
||||
mkdir -pv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} /nix/var/nix/{gcroots,profiles}/per-user
|
||||
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} /nix/var/nix/{gcroots,profiles}/per-user
|
||||
|
||||
_sudo "to make the basic directory structure of Nix (part 2)" \
|
||||
mkdir -pv -m 1775 /nix/store
|
||||
|
||||
_sudo "to make the basic directory structure of Nix (part 3)" \
|
||||
chgrp "$NIX_BUILD_GROUP_NAME" /nix/store
|
||||
install -dv -g "$NIX_BUILD_GROUP_NAME" -m 1775 /nix/store
|
||||
|
||||
_sudo "to place the default nix daemon configuration (part 1)" \
|
||||
mkdir -pv -m 0555 /etc/nix
|
||||
install -dv -m 0555 /etc/nix
|
||||
}
|
||||
|
||||
place_channel_configuration() {
|
||||
|
@ -473,7 +597,7 @@ This installation tool will set up your computer with the Nix package
|
|||
manager. This will happen in a few stages:
|
||||
|
||||
1. Make sure your computer doesn't already have Nix. If it does, I
|
||||
will show you instructions on how to clean up your old one.
|
||||
will show you instructions on how to clean up your old install.
|
||||
|
||||
2. Show you what we are going to install and where. Then we will ask
|
||||
if you are ready to continue.
|
||||
|
@ -572,6 +696,7 @@ EOF
|
|||
}
|
||||
|
||||
install_from_extracted_nix() {
|
||||
task "Installing Nix"
|
||||
(
|
||||
cd "$EXTRACTED_NIX_PATH"
|
||||
|
||||
|
@ -587,9 +712,8 @@ $NIX_INSTALLED_NIX.
|
|||
EOF
|
||||
fi
|
||||
|
||||
cat ./.reginfo \
|
||||
| _sudo "to load data for the first time in to the Nix Database" \
|
||||
"$NIX_INSTALLED_NIX/bin/nix-store" --load-db
|
||||
_sudo "to load data for the first time in to the Nix Database" \
|
||||
"$NIX_INSTALLED_NIX/bin/nix-store" --load-db < ./.reginfo
|
||||
|
||||
echo " Just finished getting the nix database ready."
|
||||
)
|
||||
|
@ -608,6 +732,7 @@ EOF
|
|||
}
|
||||
|
||||
configure_shell_profile() {
|
||||
task "Setting up shell profiles: ${PROFILE_TARGETS[*]}"
|
||||
for profile_target in "${PROFILE_TARGETS[@]}"; do
|
||||
if [ -e "$profile_target" ]; then
|
||||
_sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \
|
||||
|
@ -627,14 +752,27 @@ configure_shell_profile() {
|
|||
tee -a "$profile_target"
|
||||
fi
|
||||
done
|
||||
# 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
|
||||
# immediately notice if something didn't get set up right?
|
||||
reminder "Nix won't work in active shell sessions until you restart them."
|
||||
}
|
||||
|
||||
cert_in_store() {
|
||||
# in a subshell
|
||||
# - change into the cert-file dir
|
||||
# - get the phyiscal pwd
|
||||
# and test if this path is in the Nix store
|
||||
[[ "$(cd -- "$(dirname "$NIX_SSL_CERT_FILE")" && exec pwd -P)" == "$NIX_ROOT/store/"* ]]
|
||||
}
|
||||
|
||||
setup_default_profile() {
|
||||
_sudo "to installing a bootstrapping Nix in to the default Profile" \
|
||||
task "Setting up the default profile"
|
||||
_sudo "to install a bootstrapping Nix in to the default profile" \
|
||||
HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_NIX"
|
||||
|
||||
if [ -z "${NIX_SSL_CERT_FILE:-}" ] || ! [ -f "${NIX_SSL_CERT_FILE:-}" ]; then
|
||||
_sudo "to installing a bootstrapping SSL certificate just for Nix in to the default Profile" \
|
||||
if [ -z "${NIX_SSL_CERT_FILE:-}" ] || ! [ -f "${NIX_SSL_CERT_FILE:-}" ] || cert_in_store; then
|
||||
_sudo "to install a bootstrapping SSL certificate just for Nix in to the default profile" \
|
||||
HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_CACERT"
|
||||
export NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt
|
||||
fi
|
||||
|
@ -643,9 +781,13 @@ setup_default_profile() {
|
|||
# Have to explicitly pass NIX_SSL_CERT_FILE as part of the sudo call,
|
||||
# otherwise it will be lost in environments where sudo doesn't pass
|
||||
# all the environment variables by default.
|
||||
_sudo "to update the default channel in the default profile" \
|
||||
HOME="$ROOT_HOME" NIX_SSL_CERT_FILE="$NIX_SSL_CERT_FILE" "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs \
|
||||
|| channel_update_failed=1
|
||||
if ! _sudo "to update the default channel in the default profile" \
|
||||
HOME="$ROOT_HOME" NIX_SSL_CERT_FILE="$NIX_SSL_CERT_FILE" "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs; then
|
||||
reminder <<EOF
|
||||
I had trouble fetching the nixpkgs channel (are you offline?)
|
||||
To try again later, run: sudo -i nix-channel --update nixpkgs
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -660,6 +802,17 @@ EOF
|
|||
}
|
||||
|
||||
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. We will call sudo
|
||||
when we need to.
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
# shellcheck source=./install-darwin-multi-user.sh
|
||||
. "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh"
|
||||
|
@ -673,17 +826,24 @@ main() {
|
|||
welcome_to_nix
|
||||
chat_about_sudo
|
||||
|
||||
cure_artifacts
|
||||
# TODO: there's a tension between cure and validate. I moved the
|
||||
# the sudo/root check out of validate to the head of this func.
|
||||
# Cure is *intended* to subsume the validate-and-abort approach,
|
||||
# so it may eventually obsolete it.
|
||||
validate_starting_assumptions
|
||||
|
||||
setup_report
|
||||
|
||||
if ! ui_confirm "Ready to continue?"; then
|
||||
ok "Alright, no changes have been made :)"
|
||||
contactme
|
||||
get_help
|
||||
trap finish_cleanup EXIT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
poly_prepare_to_install
|
||||
|
||||
create_build_group
|
||||
create_build_users
|
||||
create_directories
|
||||
|
@ -693,6 +853,7 @@ main() {
|
|||
configure_shell_profile
|
||||
|
||||
set +eu
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/profile
|
||||
set -eu
|
||||
|
||||
|
@ -704,5 +865,20 @@ main() {
|
|||
trap finish_success EXIT
|
||||
}
|
||||
|
||||
# set an empty initial arg for bare invocations in case we need to
|
||||
# disambiguate someone directly invoking this later.
|
||||
if [ "${#@}" = 0 ]; then
|
||||
set ""
|
||||
fi
|
||||
|
||||
main
|
||||
# ACTION for override
|
||||
case "${1-}" in
|
||||
# uninstall)
|
||||
# shift
|
||||
# uninstall "$@";;
|
||||
# install == same as the no-arg condition for now (but, explicit)
|
||||
""|install)
|
||||
main;;
|
||||
*) # holding space for future options (like uninstall + install?)
|
||||
failure "install-multi-user: invalid argument";;
|
||||
esac
|
||||
|
|
|
@ -26,18 +26,9 @@ fi
|
|||
|
||||
# macOS support for 10.12.6 or higher
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
IFS='.' read macos_major macos_minor macos_patch << EOF
|
||||
IFS='.' read -r macos_major macos_minor macos_patch << EOF
|
||||
$(sw_vers -productVersion)
|
||||
EOF
|
||||
# TODO: this is a temporary speed-bump to keep people from naively installing Nix
|
||||
# on macOS Big Sur (11.0+, 10.16+) until nixpkgs updates are ready for them.
|
||||
# *Ideally* this is gone before next Nix release. If you're intentionally working on
|
||||
# Nix + Big Sur, just comment out this block and be on your way :)
|
||||
if [ "$macos_major" -gt 10 ] || { [ "$macos_major" -eq 10 ] && [ "$macos_minor" -gt 15 ]; }; then
|
||||
echo "$0: nixpkgs isn't quite ready to support macOS $(sw_vers -productVersion) yet"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$macos_major" -lt 10 ] || { [ "$macos_major" -eq 10 ] && [ "$macos_minor" -lt 12 ]; } || { [ "$macos_minor" -eq 12 ] && [ "$macos_patch" -lt 6 ]; }; then
|
||||
# patch may not be present; command substitution for simplicity
|
||||
echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.12.6 or higher"
|
||||
|
@ -46,21 +37,40 @@ EOF
|
|||
fi
|
||||
|
||||
# Determine if we could use the multi-user installer or not
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
echo "Note: a multi-user installation is possible. See https://nixos.org/nix/manual/#sect-multi-user-installation" >&2
|
||||
elif [ "$(uname -s)" = "Linux" ]; then
|
||||
if [ "$(uname -s)" = "Linux" ]; then
|
||||
echo "Note: a multi-user installation is possible. See https://nixos.org/nix/manual/#sect-multi-user-installation" >&2
|
||||
fi
|
||||
|
||||
INSTALL_MODE=no-daemon
|
||||
CREATE_DARWIN_VOLUME=0
|
||||
case "$(uname -s)" in
|
||||
"Darwin")
|
||||
INSTALL_MODE=daemon;;
|
||||
*)
|
||||
INSTALL_MODE=no-daemon;;
|
||||
esac
|
||||
|
||||
# space-separated string
|
||||
ACTIONS=
|
||||
|
||||
# handle the command line flags
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
--daemon)
|
||||
INSTALL_MODE=daemon;;
|
||||
INSTALL_MODE=daemon
|
||||
ACTIONS="${ACTIONS}install "
|
||||
;;
|
||||
--no-daemon)
|
||||
INSTALL_MODE=no-daemon;;
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
printf '\e[1;31mError: --no-daemon installs are no-longer supported on Darwin/macOS!\e[0m\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
INSTALL_MODE=no-daemon
|
||||
# intentional tail space
|
||||
ACTIONS="${ACTIONS}install "
|
||||
;;
|
||||
# --uninstall)
|
||||
# # intentional tail space
|
||||
# ACTIONS="${ACTIONS}uninstall "
|
||||
# ;;
|
||||
--no-channel-add)
|
||||
export NIX_INSTALLER_NO_CHANNEL_ADD=1;;
|
||||
--daemon-user-count)
|
||||
|
@ -69,13 +79,18 @@ while [ $# -gt 0 ]; do
|
|||
--no-modify-profile)
|
||||
NIX_INSTALLER_NO_MODIFY_PROFILE=1;;
|
||||
--darwin-use-unencrypted-nix-store-volume)
|
||||
CREATE_DARWIN_VOLUME=1;;
|
||||
{
|
||||
echo "Warning: the flag --darwin-use-unencrypted-nix-store-volume"
|
||||
echo " is no longer needed and will be removed in the future."
|
||||
echo ""
|
||||
} >&2;;
|
||||
--nix-extra-conf-file)
|
||||
export NIX_EXTRA_CONF="$(cat $2)"
|
||||
# shellcheck disable=SC2155
|
||||
export NIX_EXTRA_CONF="$(cat "$2")"
|
||||
shift;;
|
||||
*)
|
||||
(
|
||||
echo "Nix Installer [--daemon|--no-daemon] [--daemon-user-count INT] [--no-channel-add] [--no-modify-profile] [--darwin-use-unencrypted-nix-store-volume] [--nix-extra-conf-file FILE]"
|
||||
{
|
||||
echo "Nix Installer [--daemon|--no-daemon] [--daemon-user-count INT] [--no-channel-add] [--no-modify-profile] [--nix-extra-conf-file FILE]"
|
||||
|
||||
echo "Choose installation method."
|
||||
echo ""
|
||||
|
@ -101,45 +116,16 @@ while [ $# -gt 0 ]; do
|
|||
if [ -n "${INVOKED_FROM_INSTALL_IN:-}" ]; then
|
||||
echo " --tarball-url-prefix URL: Base URL to download the Nix tarball from."
|
||||
fi
|
||||
) >&2
|
||||
} >&2
|
||||
|
||||
# darwin and Catalina+
|
||||
if [ "$(uname -s)" = "Darwin" ] && { [ "$macos_major" -gt 10 ] || { [ "$macos_major" -eq 10 ] && [ "$macos_minor" -gt 14 ]; }; }; then
|
||||
(
|
||||
echo " --darwin-use-unencrypted-nix-store-volume: Create an APFS volume for the Nix"
|
||||
echo " store and mount it at /nix. This is the recommended way to create"
|
||||
echo " /nix with a read-only / on macOS >=10.15."
|
||||
echo " See: https://nixos.org/nix/manual/#sect-macos-installation"
|
||||
echo ""
|
||||
) >&2
|
||||
fi
|
||||
exit;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
if [ "$CREATE_DARWIN_VOLUME" = 1 ]; then
|
||||
printf '\e[1;31mCreating volume and mountpoint /nix.\e[0m\n'
|
||||
"$self/create-darwin-volume.sh"
|
||||
fi
|
||||
|
||||
writable="$(diskutil info -plist / | xmllint --xpath "name(/plist/dict/key[text()='Writable']/following-sibling::*[1])" -)"
|
||||
if ! [ -e $dest ] && [ "$writable" = "false" ]; then
|
||||
(
|
||||
echo ""
|
||||
echo "Installing on macOS >=10.15 requires relocating the store to an apfs volume."
|
||||
echo "Use sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume or run the preparation steps manually."
|
||||
echo "See https://nixos.org/nix/manual/#sect-macos-installation"
|
||||
echo ""
|
||||
) >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$INSTALL_MODE" = "daemon" ]; then
|
||||
printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n'
|
||||
exec "$self/install-multi-user"
|
||||
exec "$self/install-multi-user" $ACTIONS # let ACTIONS split
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
@ -194,6 +180,7 @@ if ! "$nix/bin/nix-store" --load-db < "$self/.reginfo"; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck source=./nix-profile.sh.in
|
||||
. "$nix/etc/profile.d/nix.sh"
|
||||
|
||||
if ! "$nix/bin/nix-env" -i "$nix"; then
|
||||
|
|
|
@ -41,10 +41,8 @@ handle_network_proxy() {
|
|||
fi
|
||||
}
|
||||
|
||||
poly_validate_assumptions() {
|
||||
if [ "$(uname -s)" != "Linux" ]; then
|
||||
failure "This script is for use with Linux!"
|
||||
fi
|
||||
poly_cure_artifacts() {
|
||||
:
|
||||
}
|
||||
|
||||
poly_service_installed_check() {
|
||||
|
@ -81,19 +79,10 @@ poly_extra_try_me_commands(){
|
|||
EOF
|
||||
fi
|
||||
}
|
||||
poly_extra_setup_instructions(){
|
||||
if [ -e /run/systemd/system ]; then
|
||||
:
|
||||
else
|
||||
cat <<EOF
|
||||
Additionally, you may want to add nix-daemon to your init-system.
|
||||
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
poly_configure_nix_daemon_service() {
|
||||
if [ -e /run/systemd/system ]; then
|
||||
task "Setting up the nix-daemon systemd service"
|
||||
_sudo "to set up the nix-daemon service" \
|
||||
systemctl link "/nix/var/nix/profiles/default$SERVICE_SRC"
|
||||
|
||||
|
@ -110,6 +99,8 @@ poly_configure_nix_daemon_service() {
|
|||
|
||||
_sudo "to start the nix-daemon.service" \
|
||||
systemctl restart nix-daemon.service
|
||||
else
|
||||
reminder "I don't support your init system yet; you may want to add nix-daemon manually."
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -207,3 +198,7 @@ poly_create_build_user() {
|
|||
--password "!" \
|
||||
"$username"
|
||||
}
|
||||
|
||||
poly_prepare_to_install() {
|
||||
:
|
||||
}
|
||||
|
|
|
@ -46,15 +46,9 @@ case "$(uname -s).$(uname -m)" in
|
|||
system=x86_64-darwin
|
||||
;;
|
||||
Darwin.arm64|Darwin.aarch64)
|
||||
# check for Rosetta 2 support
|
||||
if ! [ -f /Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist ]; then
|
||||
oops "Rosetta 2 is not installed on this ARM64 macOS machine. Run softwareupdate --install-rosetta then restart installation"
|
||||
fi
|
||||
|
||||
hash=@binaryTarball_x86_64-darwin@
|
||||
path=@tarballPath_x86_64-darwin@
|
||||
# eventually maybe: aarch64-darwin
|
||||
system=x86_64-darwin
|
||||
hash=@binaryTarball_aarch64-darwin@
|
||||
path=@tarballPath_aarch64-darwin@
|
||||
system=aarch64-darwin
|
||||
;;
|
||||
*) oops "sorry, there is no binary distribution of Nix for your platform";;
|
||||
esac
|
||||
|
|
|
@ -277,6 +277,15 @@ connected:
|
|||
|
||||
auto drv = store->readDerivation(*drvPath);
|
||||
auto outputHashes = staticOutputHashes(*store, drv);
|
||||
|
||||
// Hijack the inputs paths of the derivation to include all the paths
|
||||
// that come from the `inputDrvs` set.
|
||||
// We don’t do that for the derivations whose `inputDrvs` is empty
|
||||
// because
|
||||
// 1. It’s not needed
|
||||
// 2. Changing the `inputSrcs` set changes the associated output ids,
|
||||
// which break CA derivations
|
||||
if (!drv.inputDrvs.empty())
|
||||
drv.inputSrcs = store->parseStorePathSet(inputs);
|
||||
|
||||
auto result = sshStore->buildDerivation(*drvPath, drv);
|
||||
|
|
|
@ -54,7 +54,7 @@ void StoreCommand::run()
|
|||
run(getStore());
|
||||
}
|
||||
|
||||
RealisedPathsCommand::RealisedPathsCommand(bool recursive)
|
||||
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
||||
: recursive(recursive)
|
||||
{
|
||||
if (recursive)
|
||||
|
@ -81,39 +81,45 @@ RealisedPathsCommand::RealisedPathsCommand(bool recursive)
|
|||
});
|
||||
}
|
||||
|
||||
void RealisedPathsCommand::run(ref<Store> store)
|
||||
void BuiltPathsCommand::run(ref<Store> store)
|
||||
{
|
||||
std::vector<RealisedPath> paths;
|
||||
BuiltPaths paths;
|
||||
if (all) {
|
||||
if (installables.size())
|
||||
throw UsageError("'--all' does not expect arguments");
|
||||
// XXX: Only uses opaque paths, ignores all the realisations
|
||||
for (auto & p : store->queryAllValidPaths())
|
||||
paths.push_back(p);
|
||||
paths.push_back(BuiltPath::Opaque{p});
|
||||
} else {
|
||||
auto pathSet = toRealisedPaths(store, realiseMode, operateOn, installables);
|
||||
paths = toBuiltPaths(store, realiseMode, operateOn, installables);
|
||||
if (recursive) {
|
||||
auto roots = std::move(pathSet);
|
||||
pathSet = {};
|
||||
RealisedPath::closure(*store, roots, pathSet);
|
||||
// XXX: This only computes the store path closure, ignoring
|
||||
// intermediate realisations
|
||||
StorePathSet pathsRoots, pathsClosure;
|
||||
for (auto & root: paths) {
|
||||
auto rootFromThis = root.outPaths();
|
||||
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
|
||||
}
|
||||
store->computeFSClosure(pathsRoots, pathsClosure);
|
||||
for (auto & path : pathsClosure)
|
||||
paths.push_back(BuiltPath::Opaque{path});
|
||||
}
|
||||
for (auto & path : pathSet)
|
||||
paths.push_back(path);
|
||||
}
|
||||
|
||||
run(store, std::move(paths));
|
||||
}
|
||||
|
||||
StorePathsCommand::StorePathsCommand(bool recursive)
|
||||
: RealisedPathsCommand(recursive)
|
||||
: BuiltPathsCommand(recursive)
|
||||
{
|
||||
}
|
||||
|
||||
void StorePathsCommand::run(ref<Store> store, std::vector<RealisedPath> paths)
|
||||
void StorePathsCommand::run(ref<Store> store, BuiltPaths paths)
|
||||
{
|
||||
StorePaths storePaths;
|
||||
for (auto & p : paths)
|
||||
storePaths.push_back(p.path());
|
||||
for (auto& builtPath : paths)
|
||||
for (auto& p : builtPath.outPaths())
|
||||
storePaths.push_back(p);
|
||||
|
||||
run(store, std::move(storePaths));
|
||||
}
|
||||
|
@ -162,7 +168,7 @@ void MixProfile::updateProfile(const StorePath & storePath)
|
|||
profile2, storePath));
|
||||
}
|
||||
|
||||
void MixProfile::updateProfile(const Buildables & buildables)
|
||||
void MixProfile::updateProfile(const BuiltPaths & buildables)
|
||||
{
|
||||
if (!profile) return;
|
||||
|
||||
|
@ -170,18 +176,15 @@ void MixProfile::updateProfile(const Buildables & buildables)
|
|||
|
||||
for (auto & buildable : buildables) {
|
||||
std::visit(overloaded {
|
||||
[&](BuildableOpaque bo) {
|
||||
[&](BuiltPath::Opaque bo) {
|
||||
result.push_back(bo.path);
|
||||
},
|
||||
[&](BuildableFromDrv bfd) {
|
||||
[&](BuiltPath::Built bfd) {
|
||||
for (auto & output : bfd.outputs) {
|
||||
/* Output path should be known because we just tried to
|
||||
build it. */
|
||||
assert(output.second);
|
||||
result.push_back(*output.second);
|
||||
result.push_back(output.second);
|
||||
}
|
||||
},
|
||||
}, buildable);
|
||||
}, buildable.raw());
|
||||
}
|
||||
|
||||
if (result.size() != 1)
|
||||
|
|
|
@ -143,7 +143,7 @@ private:
|
|||
};
|
||||
|
||||
/* A command that operates on zero or more store paths. */
|
||||
struct RealisedPathsCommand : public InstallablesCommand
|
||||
struct BuiltPathsCommand : public InstallablesCommand
|
||||
{
|
||||
private:
|
||||
|
||||
|
@ -156,26 +156,26 @@ protected:
|
|||
|
||||
public:
|
||||
|
||||
RealisedPathsCommand(bool recursive = false);
|
||||
BuiltPathsCommand(bool recursive = false);
|
||||
|
||||
using StoreCommand::run;
|
||||
|
||||
virtual void run(ref<Store> store, std::vector<RealisedPath> paths) = 0;
|
||||
virtual void run(ref<Store> store, BuiltPaths paths) = 0;
|
||||
|
||||
void run(ref<Store> store) override;
|
||||
|
||||
bool useDefaultInstallables() override { return !all; }
|
||||
};
|
||||
|
||||
struct StorePathsCommand : public RealisedPathsCommand
|
||||
struct StorePathsCommand : public BuiltPathsCommand
|
||||
{
|
||||
StorePathsCommand(bool recursive = false);
|
||||
|
||||
using RealisedPathsCommand::run;
|
||||
using BuiltPathsCommand::run;
|
||||
|
||||
virtual void run(ref<Store> store, std::vector<StorePath> storePaths) = 0;
|
||||
|
||||
void run(ref<Store> store, std::vector<RealisedPath> paths) override;
|
||||
void run(ref<Store> store, BuiltPaths paths) override;
|
||||
};
|
||||
|
||||
/* A command that operates on exactly one store path. */
|
||||
|
@ -216,7 +216,7 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
|
|||
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
|
||||
}
|
||||
|
||||
Buildables build(ref<Store> store, Realise mode,
|
||||
BuiltPaths build(ref<Store> store, Realise mode,
|
||||
std::vector<std::shared_ptr<Installable>> installables, BuildMode bMode = bmNormal);
|
||||
|
||||
std::set<StorePath> toStorePaths(ref<Store> store,
|
||||
|
@ -231,7 +231,7 @@ std::set<StorePath> toDerivations(ref<Store> store,
|
|||
std::vector<std::shared_ptr<Installable>> installables,
|
||||
bool useDeriver = false);
|
||||
|
||||
std::set<RealisedPath> toRealisedPaths(
|
||||
BuiltPaths toBuiltPaths(
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
|
@ -252,7 +252,7 @@ struct MixProfile : virtual StoreCommand
|
|||
|
||||
/* If 'profile' is set, make it point at the store path produced
|
||||
by 'buildables'. */
|
||||
void updateProfile(const Buildables & buildables);
|
||||
void updateProfile(const BuiltPaths & buildables);
|
||||
};
|
||||
|
||||
struct MixDefaultProfile : MixProfile
|
||||
|
|
|
@ -20,31 +20,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
nlohmann::json BuildableOpaque::toJSON(ref<Store> store) const {
|
||||
nlohmann::json res;
|
||||
res["path"] = store->printStorePath(path);
|
||||
return res;
|
||||
}
|
||||
|
||||
nlohmann::json BuildableFromDrv::toJSON(ref<Store> store) const {
|
||||
nlohmann::json res;
|
||||
res["drvPath"] = store->printStorePath(drvPath);
|
||||
for (const auto& [output, path] : outputs) {
|
||||
res["outputs"][output] = path ? store->printStorePath(*path) : "";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
nlohmann::json buildablesToJSON(const Buildables & buildables, ref<Store> store) {
|
||||
auto res = nlohmann::json::array();
|
||||
for (const Buildable & buildable : buildables) {
|
||||
std::visit([&res, store](const auto & buildable) {
|
||||
res.push_back(buildable.toJSON(store));
|
||||
}, buildable);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void completeFlakeInputPath(
|
||||
ref<EvalState> evalState,
|
||||
const FlakeRef & flakeRef,
|
||||
|
@ -111,10 +86,11 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
|
||||
addFlag({
|
||||
.longName = "override-input",
|
||||
.description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`).",
|
||||
.description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`). This implies `--no-write-lock-file`.",
|
||||
.category = category,
|
||||
.labels = {"input-path", "flake-url"},
|
||||
.handler = {[&](std::string inputPath, std::string flakeRef) {
|
||||
lockFlags.writeLockFile = false;
|
||||
lockFlags.inputOverrides.insert_or_assign(
|
||||
flake::parseInputPath(inputPath),
|
||||
parseFlakeRef(flakeRef, absPath(".")));
|
||||
|
@ -309,9 +285,9 @@ void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
|||
}
|
||||
}
|
||||
|
||||
Buildable Installable::toBuildable()
|
||||
DerivedPath Installable::toDerivedPath()
|
||||
{
|
||||
auto buildables = toBuildables();
|
||||
auto buildables = toDerivedPaths();
|
||||
if (buildables.size() != 1)
|
||||
throw Error("installable '%s' evaluates to %d derivations, where only one is expected", what(), buildables.size());
|
||||
return std::move(buildables[0]);
|
||||
|
@ -345,22 +321,19 @@ struct InstallableStorePath : Installable
|
|||
|
||||
std::string what() override { return store->printStorePath(storePath); }
|
||||
|
||||
Buildables toBuildables() override
|
||||
DerivedPaths toDerivedPaths() override
|
||||
{
|
||||
if (storePath.isDerivation()) {
|
||||
std::map<std::string, std::optional<StorePath>> outputs;
|
||||
auto drv = store->readDerivation(storePath);
|
||||
for (auto & [name, output] : drv.outputsAndOptPaths(*store))
|
||||
outputs.emplace(name, output.second);
|
||||
return {
|
||||
BuildableFromDrv {
|
||||
DerivedPath::Built {
|
||||
.drvPath = storePath,
|
||||
.outputs = std::move(outputs)
|
||||
.outputs = drv.outputNames(),
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
BuildableOpaque {
|
||||
DerivedPath::Opaque {
|
||||
.path = storePath,
|
||||
}
|
||||
};
|
||||
|
@ -373,22 +346,22 @@ struct InstallableStorePath : Installable
|
|||
}
|
||||
};
|
||||
|
||||
Buildables InstallableValue::toBuildables()
|
||||
DerivedPaths InstallableValue::toDerivedPaths()
|
||||
{
|
||||
Buildables res;
|
||||
DerivedPaths res;
|
||||
|
||||
std::map<StorePath, std::map<std::string, std::optional<StorePath>>> drvsToOutputs;
|
||||
std::map<StorePath, std::set<std::string>> drvsToOutputs;
|
||||
|
||||
// Group by derivation, helps with .all in particular
|
||||
for (auto & drv : toDerivations()) {
|
||||
auto outputName = drv.outputName;
|
||||
if (outputName == "")
|
||||
throw Error("derivation '%s' lacks an 'outputName' attribute", state->store->printStorePath(drv.drvPath));
|
||||
drvsToOutputs[drv.drvPath].insert_or_assign(outputName, drv.outPath);
|
||||
drvsToOutputs[drv.drvPath].insert(outputName);
|
||||
}
|
||||
|
||||
for (auto & i : drvsToOutputs)
|
||||
res.push_back(BuildableFromDrv { i.first, i.second });
|
||||
res.push_back(DerivedPath::Built { i.first, i.second });
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -527,7 +500,11 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
|||
auto root = cache->getRoot();
|
||||
|
||||
for (auto & attrPath : getActualAttrPaths()) {
|
||||
auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath));
|
||||
auto attr = root->findAlongAttrPath(
|
||||
parseAttrPath(*state, attrPath),
|
||||
true
|
||||
);
|
||||
|
||||
if (!attr) continue;
|
||||
|
||||
if (!attr->isDerivation())
|
||||
|
@ -695,31 +672,67 @@ std::shared_ptr<Installable> SourceExprCommand::parseInstallable(
|
|||
return installables.front();
|
||||
}
|
||||
|
||||
Buildables build(ref<Store> store, Realise mode,
|
||||
BuiltPaths getBuiltPaths(ref<Store> store, DerivedPaths hopefullyBuiltPaths)
|
||||
{
|
||||
BuiltPaths res;
|
||||
for (auto& b : hopefullyBuiltPaths)
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](DerivedPath::Opaque bo) {
|
||||
res.push_back(BuiltPath::Opaque{bo.path});
|
||||
},
|
||||
[&](DerivedPath::Built bfd) {
|
||||
OutputPathMap outputs;
|
||||
auto drv = store->readDerivation(bfd.drvPath);
|
||||
auto outputHashes = staticOutputHashes(*store, drv);
|
||||
auto drvOutputs = drv.outputsAndOptPaths(*store);
|
||||
for (auto& output : bfd.outputs) {
|
||||
if (!outputHashes.count(output))
|
||||
throw Error(
|
||||
"the derivation '%s' doesn't have an output "
|
||||
"named '%s'",
|
||||
store->printStorePath(bfd.drvPath), output);
|
||||
if (settings.isExperimentalFeatureEnabled(
|
||||
"ca-derivations")) {
|
||||
auto outputId =
|
||||
DrvOutput{outputHashes.at(output), output};
|
||||
auto realisation =
|
||||
store->queryRealisation(outputId);
|
||||
if (!realisation)
|
||||
throw Error(
|
||||
"cannot operate on an output of unbuilt "
|
||||
"content-addresed derivation '%s'",
|
||||
outputId.to_string());
|
||||
outputs.insert_or_assign(
|
||||
output, realisation->outPath);
|
||||
} else {
|
||||
// If ca-derivations isn't enabled, assume that
|
||||
// the output path is statically known.
|
||||
assert(drvOutputs.count(output));
|
||||
assert(drvOutputs.at(output).second);
|
||||
outputs.insert_or_assign(
|
||||
output, *drvOutputs.at(output).second);
|
||||
}
|
||||
}
|
||||
res.push_back(BuiltPath::Built{bfd.drvPath, outputs});
|
||||
},
|
||||
},
|
||||
b.raw());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
BuiltPaths build(ref<Store> store, Realise mode,
|
||||
std::vector<std::shared_ptr<Installable>> installables, BuildMode bMode)
|
||||
{
|
||||
if (mode == Realise::Nothing)
|
||||
settings.readOnlyMode = true;
|
||||
|
||||
Buildables buildables;
|
||||
|
||||
std::vector<StorePathWithOutputs> pathsToBuild;
|
||||
std::vector<DerivedPath> pathsToBuild;
|
||||
|
||||
for (auto & i : installables) {
|
||||
for (auto & b : i->toBuildables()) {
|
||||
std::visit(overloaded {
|
||||
[&](BuildableOpaque bo) {
|
||||
pathsToBuild.push_back({bo.path});
|
||||
},
|
||||
[&](BuildableFromDrv bfd) {
|
||||
StringSet outputNames;
|
||||
for (auto & output : bfd.outputs)
|
||||
outputNames.insert(output.first);
|
||||
pathsToBuild.push_back({bfd.drvPath, outputNames});
|
||||
},
|
||||
}, b);
|
||||
buildables.push_back(std::move(b));
|
||||
}
|
||||
auto b = i->toDerivedPaths();
|
||||
pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end());
|
||||
}
|
||||
|
||||
if (mode == Realise::Nothing)
|
||||
|
@ -727,68 +740,37 @@ Buildables build(ref<Store> store, Realise mode,
|
|||
else if (mode == Realise::Outputs)
|
||||
store->buildPaths(pathsToBuild, bMode);
|
||||
|
||||
return buildables;
|
||||
return getBuiltPaths(store, pathsToBuild);
|
||||
}
|
||||
|
||||
std::set<RealisedPath> toRealisedPaths(
|
||||
BuiltPaths toBuiltPaths(
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
std::vector<std::shared_ptr<Installable>> installables)
|
||||
{
|
||||
std::set<RealisedPath> res;
|
||||
if (operateOn == OperateOn::Output) {
|
||||
for (auto & b : build(store, mode, installables))
|
||||
std::visit(overloaded {
|
||||
[&](BuildableOpaque bo) {
|
||||
res.insert(bo.path);
|
||||
},
|
||||
[&](BuildableFromDrv bfd) {
|
||||
auto drv = store->readDerivation(bfd.drvPath);
|
||||
auto outputHashes = staticOutputHashes(*store, drv);
|
||||
for (auto & output : bfd.outputs) {
|
||||
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||
if (!outputHashes.count(output.first))
|
||||
throw Error(
|
||||
"the derivation '%s' doesn't have an output named '%s'",
|
||||
store->printStorePath(bfd.drvPath),
|
||||
output.first);
|
||||
auto outputId = DrvOutput{outputHashes.at(output.first), output.first};
|
||||
auto realisation = store->queryRealisation(outputId);
|
||||
if (!realisation)
|
||||
throw Error("cannot operate on an output of unbuilt content-addresed derivation '%s'", outputId.to_string());
|
||||
res.insert(RealisedPath{*realisation});
|
||||
}
|
||||
else {
|
||||
// If ca-derivations isn't enabled, behave as if
|
||||
// all the paths are opaque to keep the default
|
||||
// behavior
|
||||
assert(output.second);
|
||||
res.insert(*output.second);
|
||||
}
|
||||
}
|
||||
},
|
||||
}, b);
|
||||
return build(store, mode, installables);
|
||||
} else {
|
||||
if (mode == Realise::Nothing)
|
||||
settings.readOnlyMode = true;
|
||||
|
||||
for (auto & i : installables)
|
||||
for (auto & b : i->toBuildables())
|
||||
if (auto bfd = std::get_if<BuildableFromDrv>(&b))
|
||||
res.insert(bfd->drvPath);
|
||||
}
|
||||
|
||||
BuiltPaths res;
|
||||
for (auto & drvPath : toDerivations(store, installables, true))
|
||||
res.push_back(BuiltPath::Opaque{drvPath});
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
StorePathSet toStorePaths(ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
std::vector<std::shared_ptr<Installable>> installables)
|
||||
{
|
||||
StorePathSet outPaths;
|
||||
for (auto & path : toRealisedPaths(store, mode, operateOn, installables))
|
||||
outPaths.insert(path.path());
|
||||
for (auto & path : toBuiltPaths(store, mode, operateOn, installables)) {
|
||||
auto thisOutPaths = path.outPaths();
|
||||
outPaths.insert(thisOutPaths.begin(), thisOutPaths.end());
|
||||
}
|
||||
return outPaths;
|
||||
}
|
||||
|
||||
|
@ -810,9 +792,9 @@ StorePathSet toDerivations(ref<Store> store,
|
|||
StorePathSet drvPaths;
|
||||
|
||||
for (auto & i : installables)
|
||||
for (auto & b : i->toBuildables())
|
||||
for (auto & b : i->toDerivedPaths())
|
||||
std::visit(overloaded {
|
||||
[&](BuildableOpaque bo) {
|
||||
[&](DerivedPath::Opaque bo) {
|
||||
if (!useDeriver)
|
||||
throw Error("argument '%s' did not evaluate to a derivation", i->what());
|
||||
auto derivers = store->queryValidDerivers(bo.path);
|
||||
|
@ -821,10 +803,10 @@ StorePathSet toDerivations(ref<Store> store,
|
|||
// FIXME: use all derivers?
|
||||
drvPaths.insert(*derivers.begin());
|
||||
},
|
||||
[&](BuildableFromDrv bfd) {
|
||||
[&](DerivedPath::Built bfd) {
|
||||
drvPaths.insert(bfd.drvPath);
|
||||
},
|
||||
}, b);
|
||||
}, b.raw());
|
||||
|
||||
return drvPaths;
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
#include "util.hh"
|
||||
#include "path.hh"
|
||||
#include "path-with-outputs.hh"
|
||||
#include "derived-path.hh"
|
||||
#include "eval.hh"
|
||||
#include "flake/flake.hh"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct DrvInfo;
|
||||
|
@ -16,25 +16,6 @@ struct SourceExprCommand;
|
|||
|
||||
namespace eval_cache { class EvalCache; class AttrCursor; }
|
||||
|
||||
struct BuildableOpaque {
|
||||
StorePath path;
|
||||
nlohmann::json toJSON(ref<Store> store) const;
|
||||
};
|
||||
|
||||
struct BuildableFromDrv {
|
||||
StorePath drvPath;
|
||||
std::map<std::string, std::optional<StorePath>> outputs;
|
||||
nlohmann::json toJSON(ref<Store> store) const;
|
||||
};
|
||||
|
||||
typedef std::variant<
|
||||
BuildableOpaque,
|
||||
BuildableFromDrv
|
||||
> Buildable;
|
||||
|
||||
typedef std::vector<Buildable> Buildables;
|
||||
nlohmann::json buildablesToJSON(const Buildables & buildables, ref<Store> store);
|
||||
|
||||
struct App
|
||||
{
|
||||
std::vector<StorePathWithOutputs> context;
|
||||
|
@ -42,17 +23,23 @@ struct App
|
|||
// FIXME: add args, sandbox settings, metadata, ...
|
||||
};
|
||||
|
||||
struct UnresolvedApp
|
||||
{
|
||||
App unresolved;
|
||||
App resolve(ref<Store>);
|
||||
};
|
||||
|
||||
struct Installable
|
||||
{
|
||||
virtual ~Installable() { }
|
||||
|
||||
virtual std::string what() = 0;
|
||||
|
||||
virtual Buildables toBuildables() = 0;
|
||||
virtual DerivedPaths toDerivedPaths() = 0;
|
||||
|
||||
Buildable toBuildable();
|
||||
DerivedPath toDerivedPath();
|
||||
|
||||
App toApp(EvalState & state);
|
||||
UnresolvedApp toApp(EvalState & state);
|
||||
|
||||
virtual std::pair<Value *, Pos> toValue(EvalState & state)
|
||||
{
|
||||
|
@ -93,7 +80,7 @@ struct InstallableValue : Installable
|
|||
|
||||
virtual std::vector<DerivationInfo> toDerivations() = 0;
|
||||
|
||||
Buildables toBuildables() override;
|
||||
DerivedPaths toDerivedPaths() override;
|
||||
};
|
||||
|
||||
struct InstallableFlake : InstallableValue
|
||||
|
|
|
@ -35,6 +35,7 @@ class Bindings
|
|||
{
|
||||
public:
|
||||
typedef uint32_t size_t;
|
||||
Pos *pos;
|
||||
|
||||
private:
|
||||
size_t size_, capacity_;
|
||||
|
|
|
@ -486,11 +486,11 @@ std::shared_ptr<AttrCursor> AttrCursor::getAttr(std::string_view name)
|
|||
return getAttr(root->state.symbols.create(name));
|
||||
}
|
||||
|
||||
std::shared_ptr<AttrCursor> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath)
|
||||
std::shared_ptr<AttrCursor> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
|
||||
{
|
||||
auto res = shared_from_this();
|
||||
for (auto & attr : attrPath) {
|
||||
res = res->maybeGetAttr(attr);
|
||||
res = res->maybeGetAttr(attr, force);
|
||||
if (!res) return {};
|
||||
}
|
||||
return res;
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
|
||||
std::shared_ptr<AttrCursor> getAttr(std::string_view name);
|
||||
|
||||
std::shared_ptr<AttrCursor> findAlongAttrPath(const std::vector<Symbol> & attrPath);
|
||||
std::shared_ptr<AttrCursor> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
|
||||
|
||||
std::string getString();
|
||||
|
||||
|
|
|
@ -201,6 +201,15 @@ string showType(const Value & v)
|
|||
}
|
||||
}
|
||||
|
||||
Pos Value::determinePos(const Pos &pos) const
|
||||
{
|
||||
switch (internalType) {
|
||||
case tAttrs: return *attrs->pos;
|
||||
case tLambda: return lambda.fun->pos;
|
||||
case tApp: return app.left->determinePos(pos);
|
||||
default: return pos;
|
||||
}
|
||||
}
|
||||
|
||||
bool Value::isTrivial() const
|
||||
{
|
||||
|
@ -1060,6 +1069,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), &i.pos));
|
||||
v.attrs->sort(); // FIXME: inefficient
|
||||
}
|
||||
|
||||
v.attrs->pos = &pos;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2091,9 +2102,12 @@ Strings EvalSettings::getDefaultNixPath()
|
|||
}
|
||||
};
|
||||
|
||||
if (!evalSettings.restrictEval && !evalSettings.pureEval) {
|
||||
add(getHome() + "/.nix-defexpr/channels");
|
||||
add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
|
||||
add(settings.nixStateDir + "/profiles/per-user/root/channels");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ static TrustedList readTrustedList()
|
|||
|
||||
static void writeTrustedList(const TrustedList & trustedList)
|
||||
{
|
||||
writeFile(trustedListPath(), nlohmann::json(trustedList).dump());
|
||||
auto path = trustedListPath();
|
||||
createDirs(dirOf(path));
|
||||
writeFile(path, nlohmann::json(trustedList).dump());
|
||||
}
|
||||
|
||||
void ConfigFile::apply()
|
||||
|
|
|
@ -113,7 +113,7 @@ struct LockFlags
|
|||
/* Whether to commit changes to flake.lock. */
|
||||
bool commitLockFile = false;
|
||||
|
||||
/* Flake inputs to be overriden. */
|
||||
/* Flake inputs to be overridden. */
|
||||
std::map<InputPath, FlakeRef> inputOverrides;
|
||||
|
||||
/* Flake inputs to be updated. This means that any existing lock
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "util.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "store-api.hh"
|
||||
#include "path-with-outputs.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
|
@ -19,7 +20,7 @@ DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs)
|
|||
DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs)
|
||||
: state(&state), attrs(nullptr), attrPath("")
|
||||
{
|
||||
auto [drvPath, selectedOutputs] = store->parsePathWithOutputs(drvPathWithOutputs);
|
||||
auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
|
||||
|
||||
this->drvPath = store->printStorePath(drvPath);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/lib
|
|||
libexpr_LIBS = libutil libstore libfetchers
|
||||
|
||||
libexpr_LDFLAGS = -lboost_context
|
||||
ifneq ($(OS), FreeBSD)
|
||||
ifeq ($(OS), Linux)
|
||||
libexpr_LDFLAGS += -ldl
|
||||
endif
|
||||
|
||||
|
|
|
@ -180,6 +180,7 @@ struct ExprOpHasAttr : Expr
|
|||
struct ExprAttrs : Expr
|
||||
{
|
||||
bool recursive;
|
||||
Pos pos;
|
||||
struct AttrDef {
|
||||
bool inherited;
|
||||
Expr * e;
|
||||
|
@ -199,7 +200,8 @@ struct ExprAttrs : Expr
|
|||
};
|
||||
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||
DynamicAttrDefs dynamicAttrs;
|
||||
ExprAttrs() : recursive(false) { };
|
||||
ExprAttrs(const Pos &pos) : recursive(false), pos(pos) { };
|
||||
ExprAttrs() : recursive(false), pos(noPos) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
|
|
@ -478,7 +478,7 @@ binds
|
|||
$$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data));
|
||||
}
|
||||
}
|
||||
| { $$ = new ExprAttrs; }
|
||||
| { $$ = new ExprAttrs(makeCurPos(@0, data)); }
|
||||
;
|
||||
|
||||
attrs
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <regex>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -35,7 +37,7 @@ InvalidPathError::InvalidPathError(const Path & path) :
|
|||
|
||||
void EvalState::realiseContext(const PathSet & context)
|
||||
{
|
||||
std::vector<StorePathWithOutputs> drvs;
|
||||
std::vector<DerivedPath::Built> drvs;
|
||||
|
||||
for (auto & i : context) {
|
||||
auto [ctxS, outputName] = decodeContext(i);
|
||||
|
@ -43,7 +45,7 @@ void EvalState::realiseContext(const PathSet & context)
|
|||
if (!store->isValidPath(ctx))
|
||||
throw InvalidPathError(store->printStorePath(ctx));
|
||||
if (!outputName.empty() && ctx.isDerivation()) {
|
||||
drvs.push_back(StorePathWithOutputs{ctx, {outputName}});
|
||||
drvs.push_back({ctx, {outputName}});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,14 +53,16 @@ void EvalState::realiseContext(const PathSet & context)
|
|||
|
||||
if (!evalSettings.enableImportFromDerivation)
|
||||
throw EvalError("attempted to realize '%1%' during evaluation but 'allow-import-from-derivation' is false",
|
||||
store->printStorePath(drvs.begin()->path));
|
||||
store->printStorePath(drvs.begin()->drvPath));
|
||||
|
||||
/* For performance, prefetch all substitute info. */
|
||||
StorePathSet willBuild, willSubstitute, unknown;
|
||||
uint64_t downloadSize, narSize;
|
||||
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
std::vector<DerivedPath> buildReqs;
|
||||
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
|
||||
store->queryMissing(buildReqs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
|
||||
store->buildPaths(drvs);
|
||||
store->buildPaths(buildReqs);
|
||||
|
||||
/* Add the output of this derivations to the allowed
|
||||
paths. */
|
||||
|
@ -545,18 +549,56 @@ typedef list<Value *> ValueList;
|
|||
#endif
|
||||
|
||||
|
||||
static Bindings::iterator getAttr(
|
||||
EvalState & state,
|
||||
string funcName,
|
||||
string attrName,
|
||||
Bindings * attrSet,
|
||||
const Pos & pos)
|
||||
{
|
||||
Bindings::iterator value = attrSet->find(state.symbols.create(attrName));
|
||||
if (value == attrSet->end()) {
|
||||
hintformat errorMsg = hintfmt(
|
||||
"attribute '%s' missing for call to '%s'",
|
||||
attrName,
|
||||
funcName
|
||||
);
|
||||
|
||||
Pos aPos = *attrSet->pos;
|
||||
if (aPos == noPos) {
|
||||
throw TypeError({
|
||||
.msg = errorMsg,
|
||||
.errPos = pos,
|
||||
});
|
||||
} else {
|
||||
auto e = TypeError({
|
||||
.msg = errorMsg,
|
||||
.errPos = aPos,
|
||||
});
|
||||
|
||||
// Adding another trace for the function name to make it clear
|
||||
// which call received wrong arguments.
|
||||
e.addTrace(pos, hintfmt("while invoking '%s'", funcName));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
/* Get the start set. */
|
||||
Bindings::iterator startSet =
|
||||
args[0]->attrs->find(state.symbols.create("startSet"));
|
||||
if (startSet == args[0]->attrs->end())
|
||||
throw EvalError({
|
||||
.msg = hintfmt("attribute 'startSet' required"),
|
||||
.errPos = pos
|
||||
});
|
||||
Bindings::iterator startSet = getAttr(
|
||||
state,
|
||||
"genericClosure",
|
||||
"startSet",
|
||||
args[0]->attrs,
|
||||
pos
|
||||
);
|
||||
|
||||
state.forceList(*startSet->value, pos);
|
||||
|
||||
ValueList workSet;
|
||||
|
@ -564,13 +606,14 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
|||
workSet.push_back(startSet->value->listElems()[n]);
|
||||
|
||||
/* Get the operator. */
|
||||
Bindings::iterator op =
|
||||
args[0]->attrs->find(state.symbols.create("operator"));
|
||||
if (op == args[0]->attrs->end())
|
||||
throw EvalError({
|
||||
.msg = hintfmt("attribute 'operator' required"),
|
||||
.errPos = pos
|
||||
});
|
||||
Bindings::iterator op = getAttr(
|
||||
state,
|
||||
"genericClosure",
|
||||
"operator",
|
||||
args[0]->attrs,
|
||||
pos
|
||||
);
|
||||
|
||||
state.forceValue(*op->value, pos);
|
||||
|
||||
/* Construct the closure by applying the operator to element of
|
||||
|
@ -673,6 +716,44 @@ static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info {
|
|||
.fun = prim_addErrorContext,
|
||||
});
|
||||
|
||||
static void prim_ceil(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos));
|
||||
mkInt(v, ceil(value));
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_ceil({
|
||||
.name = "__ceil",
|
||||
.args = {"double"},
|
||||
.doc = R"(
|
||||
Converts an IEEE-754 double-precision floating-point number (*double*) to
|
||||
the next higher integer.
|
||||
|
||||
If the datatype is neither an integer nor a "float", an evaluation error will be
|
||||
thrown.
|
||||
)",
|
||||
.fun = prim_ceil,
|
||||
});
|
||||
|
||||
static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos));
|
||||
mkInt(v, floor(value));
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_floor({
|
||||
.name = "__floor",
|
||||
.args = {"double"},
|
||||
.doc = R"(
|
||||
Converts an IEEE-754 double-precision floating-point number (*double*) to
|
||||
the next lower integer.
|
||||
|
||||
If the datatype is neither an integer nor a "float", an evaluation error will be
|
||||
thrown.
|
||||
)",
|
||||
.fun = prim_floor,
|
||||
});
|
||||
|
||||
/* Try evaluating the argument. Success => {success=true; value=something;},
|
||||
* else => {success=false; value=false;} */
|
||||
static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
|
@ -814,12 +895,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
|||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
/* Figure out the name first (for stack backtraces). */
|
||||
Bindings::iterator attr = args[0]->attrs->find(state.sName);
|
||||
if (attr == args[0]->attrs->end())
|
||||
throw EvalError({
|
||||
.msg = hintfmt("required attribute 'name' missing"),
|
||||
.errPos = pos
|
||||
});
|
||||
Bindings::iterator attr = getAttr(
|
||||
state,
|
||||
"derivationStrict",
|
||||
state.sName,
|
||||
args[0]->attrs,
|
||||
pos
|
||||
);
|
||||
|
||||
string drvName;
|
||||
Pos & posDrvName(*attr->pos);
|
||||
try {
|
||||
|
@ -951,7 +1034,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
|||
}
|
||||
|
||||
} else {
|
||||
auto s = state.coerceToString(posDrvName, *i->value, context, true);
|
||||
auto s = state.coerceToString(*i->pos, *i->value, context, true);
|
||||
drv.env.emplace(key, s);
|
||||
if (i->name == state.sBuilder) drv.builder = s;
|
||||
else if (i->name == state.sSystem) drv.platform = s;
|
||||
|
@ -1208,7 +1291,10 @@ static RegisterPrimOp primop_toPath({
|
|||
static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
if (evalSettings.pureEval)
|
||||
throw EvalError("builtins.storePath' is not allowed in pure evaluation mode");
|
||||
throw EvalError({
|
||||
.msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
PathSet context;
|
||||
Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context));
|
||||
|
@ -1367,12 +1453,13 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
|||
if (i != v2.attrs->end())
|
||||
prefix = state.forceStringNoCtx(*i->value, pos);
|
||||
|
||||
i = v2.attrs->find(state.symbols.create("path"));
|
||||
if (i == v2.attrs->end())
|
||||
throw EvalError({
|
||||
.msg = hintfmt("attribute 'path' missing"),
|
||||
.errPos = pos
|
||||
});
|
||||
i = getAttr(
|
||||
state,
|
||||
"findFile",
|
||||
"path",
|
||||
v2.attrs,
|
||||
pos
|
||||
);
|
||||
|
||||
PathSet context;
|
||||
string path = state.coerceToString(pos, *i->value, context, false, false);
|
||||
|
@ -1918,26 +2005,26 @@ static RegisterPrimOp primop_path({
|
|||
An enrichment of the built-in path type, based on the attributes
|
||||
present in *args*. All are optional except `path`:
|
||||
|
||||
- path
|
||||
- path\
|
||||
The underlying path.
|
||||
|
||||
- name
|
||||
- name\
|
||||
The name of the path when added to the store. This can used to
|
||||
reference paths that have nix-illegal characters in their names,
|
||||
like `@`.
|
||||
|
||||
- filter
|
||||
- filter\
|
||||
A function of the type expected by `builtins.filterSource`,
|
||||
with the same semantics.
|
||||
|
||||
- recursive
|
||||
- recursive\
|
||||
When `false`, when `path` is added to the store it is with a
|
||||
flat hash, rather than a hash of the NAR serialization of the
|
||||
file. Thus, `path` must refer to a regular file, not a
|
||||
directory. This allows similar behavior to `fetchurl`. Defaults
|
||||
to `true`.
|
||||
|
||||
- sha256
|
||||
- sha256\
|
||||
When provided, this is the expected hash of the file at the
|
||||
path. Evaluation will fail if the hash is incorrect, and
|
||||
providing a hash allows `builtins.path` to be used even when the
|
||||
|
@ -2014,12 +2101,13 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
|||
string attr = state.forceStringNoCtx(*args[0], pos);
|
||||
state.forceAttrs(*args[1], pos);
|
||||
// !!! Should we create a symbol here or just do a lookup?
|
||||
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
|
||||
if (i == args[1]->attrs->end())
|
||||
throw EvalError({
|
||||
.msg = hintfmt("attribute '%1%' missing", attr),
|
||||
.errPos = pos
|
||||
});
|
||||
Bindings::iterator i = getAttr(
|
||||
state,
|
||||
"getAttr",
|
||||
attr,
|
||||
args[1]->attrs,
|
||||
pos
|
||||
);
|
||||
// !!! add to stack trace?
|
||||
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
|
||||
state.forceValue(*i->value, pos);
|
||||
|
@ -2146,22 +2234,25 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
|
|||
Value & v2(*args[0]->listElems()[i]);
|
||||
state.forceAttrs(v2, pos);
|
||||
|
||||
Bindings::iterator j = v2.attrs->find(state.sName);
|
||||
if (j == v2.attrs->end())
|
||||
throw TypeError({
|
||||
.msg = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
|
||||
.errPos = pos
|
||||
});
|
||||
string name = state.forceStringNoCtx(*j->value, pos);
|
||||
Bindings::iterator j = getAttr(
|
||||
state,
|
||||
"listToAttrs",
|
||||
state.sName,
|
||||
v2.attrs,
|
||||
pos
|
||||
);
|
||||
|
||||
string name = state.forceStringNoCtx(*j->value, *j->pos);
|
||||
|
||||
Symbol sym = state.symbols.create(name);
|
||||
if (seen.insert(sym).second) {
|
||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
|
||||
if (j2 == v2.attrs->end())
|
||||
throw TypeError({
|
||||
.msg = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
|
||||
.errPos = pos
|
||||
});
|
||||
Bindings::iterator j2 = getAttr(
|
||||
state,
|
||||
"listToAttrs",
|
||||
state.sValue,
|
||||
v2.attrs,
|
||||
pos
|
||||
);
|
||||
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
||||
}
|
||||
}
|
||||
|
@ -2802,7 +2893,12 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V
|
|||
for (unsigned int n = 0; n < nrLists; ++n) {
|
||||
Value * vElem = args[1]->listElems()[n];
|
||||
state.callFunction(*args[0], *vElem, lists[n], pos);
|
||||
state.forceList(lists[n], pos);
|
||||
try {
|
||||
state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)));
|
||||
} catch (TypeError &e) {
|
||||
e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap"));
|
||||
throw e;
|
||||
}
|
||||
len += lists[n].listSize();
|
||||
}
|
||||
|
||||
|
|
|
@ -303,17 +303,17 @@ static RegisterPrimOp primop_fetchGit({
|
|||
of the repo at that URL is fetched. Otherwise, it can be an
|
||||
attribute with the following attributes (all except `url` optional):
|
||||
|
||||
- url
|
||||
- url\
|
||||
The URL of the repo.
|
||||
|
||||
- name
|
||||
- name\
|
||||
The name of the directory the repo should be exported to in the
|
||||
store. Defaults to the basename of the URL.
|
||||
|
||||
- rev
|
||||
- rev\
|
||||
The git revision to fetch. Defaults to the tip of `ref`.
|
||||
|
||||
- ref
|
||||
- ref\
|
||||
The git ref to look for the requested revision under. This is
|
||||
often a branch or tag name. Defaults to `HEAD`.
|
||||
|
||||
|
@ -321,11 +321,11 @@ static RegisterPrimOp primop_fetchGit({
|
|||
of Nix 2.3.0 Nix will not prefix `refs/heads/` if `ref` starts
|
||||
with `refs/`.
|
||||
|
||||
- submodules
|
||||
- submodules\
|
||||
A Boolean parameter that specifies whether submodules should be
|
||||
checked out. Defaults to `false`.
|
||||
|
||||
- allRefs
|
||||
- allRefs\
|
||||
Whether to fetch all refs of the repository. With this argument being
|
||||
true, it's possible to load a `rev` from *any* `ref` (by default only
|
||||
`rev`s from the specified `ref` are supported).
|
||||
|
|
|
@ -341,6 +341,8 @@ public:
|
|||
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
|
||||
}
|
||||
|
||||
Pos determinePos(const Pos &pos) const;
|
||||
|
||||
/* Check whether forcing this value requires a trivial amount of
|
||||
computation. In particular, function applications are
|
||||
non-trivial. */
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace nix::fetchers {
|
||||
|
||||
typedef std::variant<std::string, uint64_t, Explicit<bool>> Attr;
|
||||
|
|
|
@ -145,13 +145,7 @@ DownloadFileResult downloadFile(
|
|||
bool immutable,
|
||||
const Headers & headers = {});
|
||||
|
||||
struct DownloadTarballMeta
|
||||
{
|
||||
time_t lastModified;
|
||||
std::string effectiveUrl;
|
||||
};
|
||||
|
||||
std::pair<Tree, DownloadTarballMeta> downloadTarball(
|
||||
std::pair<Tree, time_t> downloadTarball(
|
||||
ref<Store> store,
|
||||
const std::string & url,
|
||||
const std::string & name,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "url-parts.hh"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
|
@ -153,12 +154,14 @@ struct GitInputScheme : InputScheme
|
|||
|
||||
std::pair<bool, std::string> getActualUrl(const Input & input) const
|
||||
{
|
||||
// Don't clone file:// URIs (but otherwise treat them the
|
||||
// same as remote URIs, i.e. don't use the working tree or
|
||||
// HEAD).
|
||||
// file:// URIs are normally not cloned (but otherwise treated the
|
||||
// same as remote URIs, i.e. we don't use the working tree or
|
||||
// HEAD). Exception: If _NIX_FORCE_HTTP is set, or the repo is a bare git
|
||||
// repo, treat as a remote URI to force a clone.
|
||||
static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; // for testing
|
||||
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||
bool isLocal = url.scheme == "file" && !forceHttp;
|
||||
bool isBareRepository = url.scheme == "file" && !pathExists(url.path + "/.git");
|
||||
bool isLocal = url.scheme == "file" && !forceHttp && !isBareRepository;
|
||||
return {isLocal, isLocal ? url.path : url.base};
|
||||
}
|
||||
|
||||
|
@ -362,6 +365,8 @@ struct GitInputScheme : InputScheme
|
|||
auto fetchRef = allRefs
|
||||
? "refs/*"
|
||||
: ref->compare(0, 5, "refs/") == 0
|
||||
? *ref
|
||||
: ref == "HEAD"
|
||||
? *ref
|
||||
: "refs/heads/" + *ref;
|
||||
runProgram("git", true, { "-C", repoDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", fetchRef, fetchRef) });
|
||||
|
|
|
@ -207,16 +207,16 @@ struct GitArchiveInputScheme : InputScheme
|
|||
|
||||
auto url = getDownloadUrl(input);
|
||||
|
||||
auto [tree, meta] = downloadTarball(store, url.url, "source", true, url.headers);
|
||||
auto [tree, lastModified] = downloadTarball(store, url.url, "source", true, url.headers);
|
||||
|
||||
input.attrs.insert_or_assign("lastModified", uint64_t(meta.lastModified));
|
||||
input.attrs.insert_or_assign("lastModified", uint64_t(lastModified));
|
||||
|
||||
getCache()->add(
|
||||
store,
|
||||
immutableAttrs,
|
||||
{
|
||||
{"rev", rev->gitRev()},
|
||||
{"lastModified", uint64_t(meta.lastModified)}
|
||||
{"lastModified", uint64_t(lastModified)}
|
||||
},
|
||||
tree.storePath,
|
||||
true);
|
||||
|
|
|
@ -114,7 +114,7 @@ static std::shared_ptr<Registry> getSystemRegistry()
|
|||
|
||||
Path getUserRegistryPath()
|
||||
{
|
||||
return getHome() + "/.config/nix/registry.json";
|
||||
return getConfigDir() + "/nix/registry.json";
|
||||
}
|
||||
|
||||
std::shared_ptr<Registry> getUserRegistry()
|
||||
|
|
|
@ -109,7 +109,7 @@ DownloadFileResult downloadFile(
|
|||
};
|
||||
}
|
||||
|
||||
std::pair<Tree, DownloadTarballMeta> downloadTarball(
|
||||
std::pair<Tree, time_t> downloadTarball(
|
||||
ref<Store> store,
|
||||
const std::string & url,
|
||||
const std::string & name,
|
||||
|
@ -127,10 +127,7 @@ std::pair<Tree, DownloadTarballMeta> downloadTarball(
|
|||
if (cached && !cached->expired)
|
||||
return {
|
||||
Tree(store->toRealPath(cached->storePath), std::move(cached->storePath)),
|
||||
{
|
||||
.lastModified = time_t(getIntAttr(cached->infoAttrs, "lastModified")),
|
||||
.effectiveUrl = maybeGetStrAttr(cached->infoAttrs, "effectiveUrl").value_or(url),
|
||||
},
|
||||
getIntAttr(cached->infoAttrs, "lastModified")
|
||||
};
|
||||
|
||||
auto res = downloadFile(store, url, name, immutable, headers);
|
||||
|
@ -155,7 +152,6 @@ std::pair<Tree, DownloadTarballMeta> downloadTarball(
|
|||
|
||||
Attrs infoAttrs({
|
||||
{"lastModified", uint64_t(lastModified)},
|
||||
{"effectiveUrl", res.effectiveUrl},
|
||||
{"etag", res.etag},
|
||||
});
|
||||
|
||||
|
@ -168,10 +164,7 @@ std::pair<Tree, DownloadTarballMeta> downloadTarball(
|
|||
|
||||
return {
|
||||
Tree(store->toRealPath(*unpackedStorePath), std::move(*unpackedStorePath)),
|
||||
{
|
||||
.lastModified = lastModified,
|
||||
.effectiveUrl = res.effectiveUrl,
|
||||
},
|
||||
lastModified,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -185,7 +178,8 @@ struct TarballInputScheme : InputScheme
|
|||
&& !hasSuffix(url.path, ".tar")
|
||||
&& !hasSuffix(url.path, ".tar.gz")
|
||||
&& !hasSuffix(url.path, ".tar.xz")
|
||||
&& !hasSuffix(url.path, ".tar.bz2"))
|
||||
&& !hasSuffix(url.path, ".tar.bz2")
|
||||
&& !hasSuffix(url.path, ".tar.zst"))
|
||||
return {};
|
||||
|
||||
Input input;
|
||||
|
@ -230,11 +224,9 @@ struct TarballInputScheme : InputScheme
|
|||
return true;
|
||||
}
|
||||
|
||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
|
||||
{
|
||||
Input input(_input);
|
||||
auto [tree, meta] = downloadTarball(store, getStrAttr(input.attrs, "url"), "source", false);
|
||||
input.attrs.insert_or_assign("url", meta.effectiveUrl);
|
||||
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), "source", false).first;
|
||||
return {std::move(tree), input};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -122,6 +122,7 @@ public:
|
|||
|
||||
void log(Verbosity lvl, const FormatOrString & fs) override
|
||||
{
|
||||
if (lvl > verbosity) return;
|
||||
auto state(state_.lock());
|
||||
log(*state, lvl, fs.s);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ void printGCWarning()
|
|||
}
|
||||
|
||||
|
||||
void printMissing(ref<Store> store, const std::vector<StorePathWithOutputs> & paths, Verbosity lvl)
|
||||
void printMissing(ref<Store> store, const std::vector<DerivedPath> & paths, Verbosity lvl)
|
||||
{
|
||||
uint64_t downloadSize, narSize;
|
||||
StorePathSet willBuild, willSubstitute, unknown;
|
||||
|
@ -310,7 +310,7 @@ void printVersion(const string & programName)
|
|||
|
||||
void showManPage(const string & name)
|
||||
{
|
||||
restoreSignals();
|
||||
restoreProcessContext();
|
||||
setenv("MANPATH", settings.nixManDir.c_str(), 1);
|
||||
execlp("man", "man", name.c_str(), nullptr);
|
||||
throw SysError("command 'man %1%' failed", name.c_str());
|
||||
|
@ -373,7 +373,7 @@ RunPager::RunPager()
|
|||
throw SysError("dupping stdin");
|
||||
if (!getenv("LESS"))
|
||||
setenv("LESS", "FRSXMK", 1);
|
||||
restoreSignals();
|
||||
restoreProcessContext();
|
||||
if (pager)
|
||||
execl("/bin/sh", "sh", "-c", pager, nullptr);
|
||||
execlp("pager", "pager", nullptr);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "args.hh"
|
||||
#include "common-args.hh"
|
||||
#include "path.hh"
|
||||
#include "derived-path.hh"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
|
@ -42,7 +43,7 @@ struct StorePathWithOutputs;
|
|||
|
||||
void printMissing(
|
||||
ref<Store> store,
|
||||
const std::vector<StorePathWithOutputs> & paths,
|
||||
const std::vector<DerivedPath> & paths,
|
||||
Verbosity lvl = lvlInfo);
|
||||
|
||||
void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||
|
|
|
@ -179,6 +179,9 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
|
|||
narInfo->url = "nar/" + narInfo->fileHash->to_string(Base32, false) + ".nar"
|
||||
+ (compression == "xz" ? ".xz" :
|
||||
compression == "bzip2" ? ".bz2" :
|
||||
compression == "zstd" ? ".zst" :
|
||||
compression == "lzip" ? ".lzip" :
|
||||
compression == "lz4" ? ".lz4" :
|
||||
compression == "br" ? ".br" :
|
||||
"");
|
||||
|
||||
|
@ -447,18 +450,43 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
|
|||
|
||||
std::optional<const Realisation> BinaryCacheStore::queryRealisation(const DrvOutput & id)
|
||||
{
|
||||
if (diskCache) {
|
||||
auto [cacheOutcome, maybeCachedRealisation] =
|
||||
diskCache->lookupRealisation(getUri(), id);
|
||||
switch (cacheOutcome) {
|
||||
case NarInfoDiskCache::oValid:
|
||||
debug("Returning a cached realisation for %s", id.to_string());
|
||||
return *maybeCachedRealisation;
|
||||
case NarInfoDiskCache::oInvalid:
|
||||
debug("Returning a cached missing realisation for %s", id.to_string());
|
||||
return {};
|
||||
case NarInfoDiskCache::oUnknown:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
|
||||
auto rawOutputInfo = getFile(outputInfoFilePath);
|
||||
|
||||
if (rawOutputInfo) {
|
||||
return {Realisation::fromJSON(
|
||||
nlohmann::json::parse(*rawOutputInfo), outputInfoFilePath)};
|
||||
auto realisation = Realisation::fromJSON(
|
||||
nlohmann::json::parse(*rawOutputInfo), outputInfoFilePath);
|
||||
|
||||
if (diskCache)
|
||||
diskCache->upsertRealisation(
|
||||
getUri(), realisation);
|
||||
|
||||
return {realisation};
|
||||
} else {
|
||||
if (diskCache)
|
||||
diskCache->upsertAbsentRealisation(getUri(), id);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
|
||||
if (diskCache)
|
||||
diskCache->upsertRealisation(getUri(), info);
|
||||
auto filePath = realisationsPrefix + "/" + info.id.to_string() + ".doi";
|
||||
upsertFile(filePath, info.toJSON().dump(), "application/json");
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ private:
|
|||
protected:
|
||||
|
||||
// The prefix under which realisation infos will be stored
|
||||
const std::string realisationsPrefix = "/realisations";
|
||||
const std::string realisationsPrefix = "realisations";
|
||||
|
||||
BinaryCacheStore(const Params & params);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
|
@ -73,7 +74,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
|||
state = &DerivationGoal::getDerivation;
|
||||
name = fmt(
|
||||
"building of '%s' from .drv file",
|
||||
StorePathWithOutputs { drvPath, wantedOutputs }.to_string(worker.store));
|
||||
DerivedPath::Built { drvPath, wantedOutputs }.to_string(worker.store));
|
||||
trace("created");
|
||||
|
||||
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
|
||||
|
@ -94,7 +95,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation
|
|||
state = &DerivationGoal::haveDerivation;
|
||||
name = fmt(
|
||||
"building of '%s' from in-memory derivation",
|
||||
StorePathWithOutputs { drvPath, drv.outputNames() }.to_string(worker.store));
|
||||
DerivedPath::Built { drvPath, drv.outputNames() }.to_string(worker.store));
|
||||
trace("created");
|
||||
|
||||
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
|
||||
|
@ -170,7 +171,7 @@ void DerivationGoal::getDerivation()
|
|||
return;
|
||||
}
|
||||
|
||||
addWaitee(upcast_goal(worker.makeSubstitutionGoal(drvPath)));
|
||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(drvPath)));
|
||||
|
||||
state = &DerivationGoal::loadDerivation;
|
||||
}
|
||||
|
@ -246,14 +247,19 @@ void DerivationGoal::haveDerivation()
|
|||
through substitutes. If that doesn't work, we'll build
|
||||
them. */
|
||||
if (settings.useSubstitutes && parsedDrv->substitutesAllowed())
|
||||
for (auto & [_, status] : initialOutputs) {
|
||||
for (auto & [outputName, status] : initialOutputs) {
|
||||
if (!status.wanted) continue;
|
||||
if (!status.known) {
|
||||
warn("do not know how to query for unknown floating content-addressed derivation output yet");
|
||||
/* Nothing to wait for; tail call */
|
||||
return DerivationGoal::gaveUpOnSubstitution();
|
||||
}
|
||||
addWaitee(upcast_goal(worker.makeSubstitutionGoal(
|
||||
if (!status.known)
|
||||
addWaitee(
|
||||
upcast_goal(
|
||||
worker.makeDrvOutputSubstitutionGoal(
|
||||
DrvOutput{status.outputHash, outputName},
|
||||
buildMode == bmRepair ? Repair : NoRepair
|
||||
)
|
||||
)
|
||||
);
|
||||
else
|
||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(
|
||||
status.known->path,
|
||||
buildMode == bmRepair ? Repair : NoRepair,
|
||||
getDerivationCA(*drv))));
|
||||
|
@ -337,7 +343,7 @@ void DerivationGoal::gaveUpOnSubstitution()
|
|||
if (!settings.useSubstitutes)
|
||||
throw Error("dependency '%s' of '%s' does not exist, and substitution is disabled",
|
||||
worker.store.printStorePath(i), worker.store.printStorePath(drvPath));
|
||||
addWaitee(upcast_goal(worker.makeSubstitutionGoal(i)));
|
||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i)));
|
||||
}
|
||||
|
||||
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
||||
|
@ -388,7 +394,7 @@ void DerivationGoal::repairClosure()
|
|||
worker.store.printStorePath(i), worker.store.printStorePath(drvPath));
|
||||
auto drvPath2 = outputsToDrv.find(i);
|
||||
if (drvPath2 == outputsToDrv.end())
|
||||
addWaitee(upcast_goal(worker.makeSubstitutionGoal(i, Repair)));
|
||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair)));
|
||||
else
|
||||
addWaitee(worker.makeDerivationGoal(drvPath2->second, StringSet(), bmRepair));
|
||||
}
|
||||
|
@ -920,6 +926,9 @@ void DerivationGoal::resolvedFinished() {
|
|||
if (realisation) {
|
||||
auto newRealisation = *realisation;
|
||||
newRealisation.id = DrvOutput{initialOutputs.at(wantedOutput).outputHash, wantedOutput};
|
||||
newRealisation.signatures.clear();
|
||||
newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation->outPath);
|
||||
signRealisation(newRealisation);
|
||||
worker.store.registerDrvOutput(newRealisation);
|
||||
} else {
|
||||
// If we don't have a realisation, then it must mean that something
|
||||
|
@ -1243,9 +1252,12 @@ OutputPathMap DerivationGoal::queryDerivationOutputMap()
|
|||
void DerivationGoal::checkPathValidity()
|
||||
{
|
||||
bool checkHash = buildMode == bmRepair;
|
||||
auto wantedOutputsLeft = wantedOutputs;
|
||||
for (auto & i : queryPartialDerivationOutputMap()) {
|
||||
InitialOutput & info = initialOutputs.at(i.first);
|
||||
info.wanted = wantOutput(i.first, wantedOutputs);
|
||||
if (info.wanted)
|
||||
wantedOutputsLeft.erase(i.first);
|
||||
if (i.second) {
|
||||
auto outputPath = *i.second;
|
||||
info.known = {
|
||||
|
@ -1258,15 +1270,33 @@ void DerivationGoal::checkPathValidity()
|
|||
};
|
||||
}
|
||||
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||
if (auto real = worker.store.queryRealisation(
|
||||
DrvOutput{initialOutputs.at(i.first).outputHash, i.first})) {
|
||||
auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
|
||||
if (auto real = worker.store.queryRealisation(drvOutput)) {
|
||||
info.known = {
|
||||
.path = real->outPath,
|
||||
.status = PathStatus::Valid,
|
||||
};
|
||||
} else if (info.known && info.known->status == PathStatus::Valid) {
|
||||
// We know the output because it' a static output of the
|
||||
// derivation, and the output path is valid, but we don't have
|
||||
// its realisation stored (probably because it has been built
|
||||
// without the `ca-derivations` experimental flag)
|
||||
worker.store.registerDrvOutput(
|
||||
Realisation{
|
||||
drvOutput,
|
||||
info.known->path,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we requested all the outputs via the empty set, we are always fine.
|
||||
// If we requested specific elements, the loop above removes all the valid
|
||||
// ones, so any that are left must be invalid.
|
||||
if (!wantedOutputsLeft.empty())
|
||||
throw Error("derivation '%s' does not have wanted outputs %s",
|
||||
worker.store.printStorePath(drvPath),
|
||||
concatStringsSep(", ", quoteStrings(wantedOutputsLeft)));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -180,6 +180,9 @@ struct DerivationGoal : public Goal
|
|||
/* Open a log file and a pipe to it. */
|
||||
Path openLogFile();
|
||||
|
||||
/* Sign the newly built realisation if the store allows it */
|
||||
virtual void signRealisation(Realisation&) {}
|
||||
|
||||
/* Close the log file. */
|
||||
void closeLogFile();
|
||||
|
||||
|
|
122
src/libstore/build/drv-output-substitution-goal.cc
Normal file
122
src/libstore/build/drv-output-substitution-goal.cc
Normal file
|
@ -0,0 +1,122 @@
|
|||
#include "drv-output-substitution-goal.hh"
|
||||
#include "worker.hh"
|
||||
#include "substitution-goal.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||
: Goal(worker)
|
||||
, id(id)
|
||||
{
|
||||
state = &DrvOutputSubstitutionGoal::init;
|
||||
name = fmt("substitution of '%s'", id.to_string());
|
||||
trace("created");
|
||||
}
|
||||
|
||||
|
||||
void DrvOutputSubstitutionGoal::init()
|
||||
{
|
||||
trace("init");
|
||||
|
||||
/* If the derivation already exists, we’re done */
|
||||
if (worker.store.queryRealisation(id)) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
}
|
||||
|
||||
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
|
||||
tryNext();
|
||||
}
|
||||
|
||||
void DrvOutputSubstitutionGoal::tryNext()
|
||||
{
|
||||
trace("Trying next substituter");
|
||||
|
||||
if (subs.size() == 0) {
|
||||
/* None left. Terminate this goal and let someone else deal
|
||||
with it. */
|
||||
debug("drv output '%s' is required, but there is no substituter that can provide it", id.to_string());
|
||||
|
||||
/* Hack: don't indicate failure if there were no substituters.
|
||||
In that case the calling derivation should just do a
|
||||
build. */
|
||||
amDone(substituterFailed ? ecFailed : ecNoSubstituters);
|
||||
|
||||
if (substituterFailed) {
|
||||
worker.failedSubstitutions++;
|
||||
worker.updateProgress();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto sub = subs.front();
|
||||
subs.pop_front();
|
||||
|
||||
// FIXME: Make async
|
||||
outputInfo = sub->queryRealisation(id);
|
||||
if (!outputInfo) {
|
||||
tryNext();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto & [depId, depPath] : outputInfo->dependentRealisations) {
|
||||
if (depId != id) {
|
||||
if (auto localOutputInfo = worker.store.queryRealisation(depId);
|
||||
localOutputInfo && localOutputInfo->outPath != depPath) {
|
||||
warn(
|
||||
"substituter '%s' has an incompatible realisation for '%s', ignoring.\n"
|
||||
"Local: %s\n"
|
||||
"Remote: %s",
|
||||
sub->getUri(),
|
||||
depId.to_string(),
|
||||
worker.store.printStorePath(localOutputInfo->outPath),
|
||||
worker.store.printStorePath(depPath)
|
||||
);
|
||||
tryNext();
|
||||
return;
|
||||
}
|
||||
addWaitee(worker.makeDrvOutputSubstitutionGoal(depId));
|
||||
}
|
||||
}
|
||||
|
||||
addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
||||
|
||||
if (waitees.empty()) outPathValid();
|
||||
else state = &DrvOutputSubstitutionGoal::outPathValid;
|
||||
}
|
||||
|
||||
void DrvOutputSubstitutionGoal::outPathValid()
|
||||
{
|
||||
assert(outputInfo);
|
||||
trace("Output path substituted");
|
||||
|
||||
if (nrFailed > 0) {
|
||||
debug("The output path of the derivation output '%s' could not be substituted", id.to_string());
|
||||
amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
worker.store.registerDrvOutput(*outputInfo);
|
||||
finished();
|
||||
}
|
||||
|
||||
void DrvOutputSubstitutionGoal::finished()
|
||||
{
|
||||
trace("finished");
|
||||
amDone(ecSuccess);
|
||||
}
|
||||
|
||||
string DrvOutputSubstitutionGoal::key()
|
||||
{
|
||||
/* "a$" ensures substitution goals happen before derivation
|
||||
goals. */
|
||||
return "a$" + std::string(id.to_string());
|
||||
}
|
||||
|
||||
void DrvOutputSubstitutionGoal::work()
|
||||
{
|
||||
(this->*state)();
|
||||
}
|
||||
|
||||
}
|
50
src/libstore/build/drv-output-substitution-goal.hh
Normal file
50
src/libstore/build/drv-output-substitution-goal.hh
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include "store-api.hh"
|
||||
#include "goal.hh"
|
||||
#include "realisation.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
class Worker;
|
||||
|
||||
// Substitution of a derivation output.
|
||||
// This is done in three steps:
|
||||
// 1. Fetch the output info from a substituter
|
||||
// 2. Substitute the corresponding output path
|
||||
// 3. Register the output info
|
||||
class DrvOutputSubstitutionGoal : public Goal {
|
||||
private:
|
||||
// The drv output we're trying to substitue
|
||||
DrvOutput id;
|
||||
|
||||
// The realisation corresponding to the given output id.
|
||||
// Will be filled once we can get it.
|
||||
std::optional<Realisation> outputInfo;
|
||||
|
||||
/* The remaining substituters. */
|
||||
std::list<ref<Store>> subs;
|
||||
|
||||
/* Whether a substituter failed. */
|
||||
bool substituterFailed = false;
|
||||
|
||||
public:
|
||||
DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
|
||||
typedef void (DrvOutputSubstitutionGoal::*GoalState)();
|
||||
GoalState state;
|
||||
|
||||
void init();
|
||||
void tryNext();
|
||||
void outPathValid();
|
||||
void finished();
|
||||
|
||||
void timedOut(Error && ex) override { abort(); };
|
||||
|
||||
string key() override;
|
||||
|
||||
void work() override;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -6,16 +6,20 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
void Store::buildPaths(const std::vector<StorePathWithOutputs> & drvPaths, BuildMode buildMode)
|
||||
void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode)
|
||||
{
|
||||
Worker worker(*this);
|
||||
|
||||
Goals goals;
|
||||
for (auto & path : drvPaths) {
|
||||
if (path.path.isDerivation())
|
||||
goals.insert(worker.makeDerivationGoal(path.path, path.outputs, buildMode));
|
||||
else
|
||||
goals.insert(worker.makeSubstitutionGoal(path.path, buildMode == bmRepair ? Repair : NoRepair));
|
||||
for (auto & br : reqs) {
|
||||
std::visit(overloaded {
|
||||
[&](DerivedPath::Built bfd) {
|
||||
goals.insert(worker.makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode));
|
||||
},
|
||||
[&](DerivedPath::Opaque bo) {
|
||||
goals.insert(worker.makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair));
|
||||
},
|
||||
}, br.raw());
|
||||
}
|
||||
|
||||
worker.run(goals);
|
||||
|
@ -31,7 +35,7 @@ void Store::buildPaths(const std::vector<StorePathWithOutputs> & drvPaths, Build
|
|||
}
|
||||
if (i->exitCode != Goal::ecSuccess) {
|
||||
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get())) failed.insert(i2->drvPath);
|
||||
else if (auto i2 = dynamic_cast<SubstitutionGoal *>(i.get())) failed.insert(i2->storePath);
|
||||
else if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get())) failed.insert(i2->storePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +94,7 @@ void Store::ensurePath(const StorePath & path)
|
|||
if (isValidPath(path)) return;
|
||||
|
||||
Worker worker(*this);
|
||||
GoalPtr goal = worker.makeSubstitutionGoal(path);
|
||||
GoalPtr goal = worker.makePathSubstitutionGoal(path);
|
||||
Goals goals = {goal};
|
||||
|
||||
worker.run(goals);
|
||||
|
@ -108,7 +112,7 @@ void Store::ensurePath(const StorePath & path)
|
|||
void LocalStore::repairPath(const StorePath & path)
|
||||
{
|
||||
Worker worker(*this);
|
||||
GoalPtr goal = worker.makeSubstitutionGoal(path, Repair);
|
||||
GoalPtr goal = worker.makePathSubstitutionGoal(path, Repair);
|
||||
Goals goals = {goal};
|
||||
|
||||
worker.run(goals);
|
||||
|
|
|
@ -78,6 +78,8 @@ void Goal::amDone(ExitCode result, std::optional<Error> ex)
|
|||
}
|
||||
waiters.clear();
|
||||
worker.removeGoal(shared_from_this());
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -100,6 +100,8 @@ struct Goal : public std::enable_shared_from_this<Goal>
|
|||
virtual string key() = 0;
|
||||
|
||||
void amDone(ExitCode result, std::optional<Error> ex = {});
|
||||
|
||||
virtual void cleanup() { }
|
||||
};
|
||||
|
||||
void addToWeakGoals(WeakGoals & goals, GoalPtr p);
|
||||
|
|
|
@ -153,6 +153,7 @@ void LocalDerivationGoal::killChild()
|
|||
void LocalDerivationGoal::tryLocalBuild() {
|
||||
unsigned int curBuilds = worker.getNrLocalBuilds();
|
||||
if (curBuilds >= settings.maxBuildJobs) {
|
||||
state = &DerivationGoal::tryToBuild;
|
||||
worker.waitForBuildSlot(shared_from_this());
|
||||
outputLocks.unlock();
|
||||
return;
|
||||
|
@ -291,7 +292,7 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull()
|
|||
auto & localStore = getLocalStore();
|
||||
uint64_t required = 8ULL * 1024 * 1024; // FIXME: make configurable
|
||||
struct statvfs st;
|
||||
if (statvfs(localStore.realStoreDir.c_str(), &st) == 0 &&
|
||||
if (statvfs(localStore.realStoreDir.get().c_str(), &st) == 0 &&
|
||||
(uint64_t) st.f_bavail * st.f_bsize < required)
|
||||
diskFull = true;
|
||||
if (statvfs(tmpDir.c_str(), &st) == 0 &&
|
||||
|
@ -416,7 +417,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
}
|
||||
|
||||
auto & localStore = getLocalStore();
|
||||
if (localStore.storeDir != localStore.realStoreDir) {
|
||||
if (localStore.storeDir != localStore.realStoreDir.get()) {
|
||||
#if __linux__
|
||||
useChroot = true;
|
||||
#else
|
||||
|
@ -581,7 +582,9 @@ void LocalDerivationGoal::startBuilder()
|
|||
throw Error("derivation '%s' requested impure path '%s', but it was not in allowed-impure-host-deps",
|
||||
worker.store.printStorePath(drvPath), i);
|
||||
|
||||
dirsInChroot[i] = i;
|
||||
/* Allow files in __impureHostDeps to be missing; e.g.
|
||||
macOS 11+ has no /usr/lib/libSystem*.dylib */
|
||||
dirsInChroot[i] = {i, true};
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
|
@ -1190,6 +1193,26 @@ void LocalDerivationGoal::writeStructuredAttrs()
|
|||
chownToBuilder(tmpDir + "/.attrs.sh");
|
||||
}
|
||||
|
||||
|
||||
static StorePath pathPartOfReq(const DerivedPath & req)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](DerivedPath::Opaque bo) {
|
||||
return bo.path;
|
||||
},
|
||||
[&](DerivedPath::Built bfd) {
|
||||
return bfd.drvPath;
|
||||
},
|
||||
}, req.raw());
|
||||
}
|
||||
|
||||
|
||||
bool LocalDerivationGoal::isAllowed(const DerivedPath & req)
|
||||
{
|
||||
return this->isAllowed(pathPartOfReq(req));
|
||||
}
|
||||
|
||||
|
||||
struct RestrictedStoreConfig : virtual LocalFSStoreConfig
|
||||
{
|
||||
using LocalFSStoreConfig::LocalFSStoreConfig;
|
||||
|
@ -1310,33 +1333,52 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
|||
std::optional<const Realisation> queryRealisation(const DrvOutput & id) override
|
||||
// XXX: This should probably be allowed if the realisation corresponds to
|
||||
// an allowed derivation
|
||||
{ throw Error("queryRealisation"); }
|
||||
{
|
||||
if (!goal.isAllowed(id))
|
||||
throw InvalidPath("cannot query an unknown output id '%s' in recursive Nix", id.to_string());
|
||||
return next->queryRealisation(id);
|
||||
}
|
||||
|
||||
void buildPaths(const std::vector<StorePathWithOutputs> & paths, BuildMode buildMode) override
|
||||
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode) override
|
||||
{
|
||||
if (buildMode != bmNormal) throw Error("unsupported build mode");
|
||||
|
||||
StorePathSet newPaths;
|
||||
std::set<Realisation> newRealisations;
|
||||
|
||||
for (auto & path : paths) {
|
||||
if (!goal.isAllowed(path.path))
|
||||
throw InvalidPath("cannot build unknown path '%s' in recursive Nix", printStorePath(path.path));
|
||||
for (auto & req : paths) {
|
||||
if (!goal.isAllowed(req))
|
||||
throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next));
|
||||
}
|
||||
|
||||
next->buildPaths(paths, buildMode);
|
||||
|
||||
for (auto & path : paths) {
|
||||
if (!path.path.isDerivation()) continue;
|
||||
auto outputs = next->queryDerivationOutputMap(path.path);
|
||||
for (auto & output : outputs)
|
||||
if (wantOutput(output.first, path.outputs))
|
||||
newPaths.insert(output.second);
|
||||
auto p = std::get_if<DerivedPath::Built>(&path);
|
||||
if (!p) continue;
|
||||
auto & bfd = *p;
|
||||
auto drv = readDerivation(bfd.drvPath);
|
||||
auto drvHashes = staticOutputHashes(*this, drv);
|
||||
auto outputs = next->queryDerivationOutputMap(bfd.drvPath);
|
||||
for (auto & [outputName, outputPath] : outputs)
|
||||
if (wantOutput(outputName, bfd.outputs)) {
|
||||
newPaths.insert(outputPath);
|
||||
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||
auto thisRealisation = next->queryRealisation(
|
||||
DrvOutput{drvHashes.at(outputName), outputName}
|
||||
);
|
||||
assert(thisRealisation);
|
||||
newRealisations.insert(*thisRealisation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StorePathSet closure;
|
||||
next->computeFSClosure(newPaths, closure);
|
||||
for (auto & path : closure)
|
||||
goal.addDependency(path);
|
||||
for (auto & real : Realisation::closure(*next, newRealisations))
|
||||
goal.addedDrvOutputs.insert(real.id);
|
||||
}
|
||||
|
||||
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||
|
@ -1358,7 +1400,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
|||
void addSignatures(const StorePath & storePath, const StringSet & sigs) override
|
||||
{ unsupported("addSignatures"); }
|
||||
|
||||
void queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||
void queryMissing(const std::vector<DerivedPath> & targets,
|
||||
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
|
||||
uint64_t & downloadSize, uint64_t & narSize) override
|
||||
{
|
||||
|
@ -1366,12 +1408,12 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
|||
client about what paths will be built/substituted or are
|
||||
already present. Probably not a big deal. */
|
||||
|
||||
std::vector<StorePathWithOutputs> allowed;
|
||||
for (auto & path : targets) {
|
||||
if (goal.isAllowed(path.path))
|
||||
allowed.emplace_back(path);
|
||||
std::vector<DerivedPath> allowed;
|
||||
for (auto & req : targets) {
|
||||
if (goal.isAllowed(req))
|
||||
allowed.emplace_back(req);
|
||||
else
|
||||
unknown.insert(path.path);
|
||||
unknown.insert(pathPartOfReq(req));
|
||||
}
|
||||
|
||||
next->queryMissing(allowed, willBuild, willSubstitute,
|
||||
|
@ -1703,18 +1745,18 @@ void LocalDerivationGoal::runChild()
|
|||
network, so give them access to /etc/resolv.conf and so
|
||||
on. */
|
||||
if (derivationIsImpure(derivationType)) {
|
||||
ss.push_back("/etc/resolv.conf");
|
||||
|
||||
// Only use nss functions to resolve hosts and
|
||||
// services. Don’t use it for anything else that may
|
||||
// be configured for this system. This limits the
|
||||
// potential impurities introduced in fixed-outputs.
|
||||
writeFile(chrootRootDir + "/etc/nsswitch.conf", "hosts: files dns\nservices: files\n");
|
||||
|
||||
ss.push_back("/etc/services");
|
||||
ss.push_back("/etc/hosts");
|
||||
if (pathExists("/var/run/nscd/socket"))
|
||||
ss.push_back("/var/run/nscd/socket");
|
||||
/* N.B. it is realistic that these paths might not exist. It
|
||||
happens when testing Nix building fixed-output derivations
|
||||
within a pure derivation. */
|
||||
for (auto & path : { "/etc/resolv.conf", "/etc/services", "/etc/hosts", "/var/run/nscd/socket" })
|
||||
if (pathExists(path))
|
||||
ss.push_back(path);
|
||||
}
|
||||
|
||||
for (auto & i : ss) dirsInChroot.emplace(i, i);
|
||||
|
@ -2276,10 +2318,6 @@ void LocalDerivationGoal::registerOutputs()
|
|||
sink.s = make_ref<std::string>(rewriteStrings(*sink.s, outputRewrites));
|
||||
StringSource source(*sink.s);
|
||||
restorePath(actualPath, source);
|
||||
|
||||
/* FIXME: set proper permissions in restorePath() so
|
||||
we don't have to do another traversal. */
|
||||
canonicalisePathMetaData(actualPath, -1, inodesSeen);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2333,32 +2371,19 @@ void LocalDerivationGoal::registerOutputs()
|
|||
}
|
||||
auto got = caSink.finish().first;
|
||||
auto refs = rewriteRefs();
|
||||
HashModuloSink narSink { htSHA256, oldHashPart };
|
||||
dumpPath(actualPath, narSink);
|
||||
auto narHashAndSize = narSink.finish();
|
||||
ValidPathInfo newInfo0 {
|
||||
worker.store.makeFixedOutputPath(
|
||||
|
||||
auto finalPath = worker.store.makeFixedOutputPath(
|
||||
outputHash.method,
|
||||
got,
|
||||
outputPathName(drv->name, outputName),
|
||||
refs.second,
|
||||
refs.first),
|
||||
narHashAndSize.first,
|
||||
};
|
||||
newInfo0.narSize = narHashAndSize.second;
|
||||
newInfo0.ca = FixedOutputHash {
|
||||
.method = outputHash.method,
|
||||
.hash = got,
|
||||
};
|
||||
newInfo0.references = refs.second;
|
||||
if (refs.first)
|
||||
newInfo0.references.insert(newInfo0.path);
|
||||
if (scratchPath != newInfo0.path) {
|
||||
refs.first);
|
||||
if (scratchPath != finalPath) {
|
||||
// Also rewrite the output path
|
||||
auto source = sinkToSource([&](Sink & nextSink) {
|
||||
StringSink sink;
|
||||
dumpPath(actualPath, sink);
|
||||
RewritingSink rsink2(oldHashPart, std::string(newInfo0.path.hashPart()), nextSink);
|
||||
RewritingSink rsink2(oldHashPart, std::string(finalPath.hashPart()), nextSink);
|
||||
rsink2(*sink.s);
|
||||
rsink2.flush();
|
||||
});
|
||||
|
@ -2368,6 +2393,21 @@ void LocalDerivationGoal::registerOutputs()
|
|||
movePath(tmpPath, actualPath);
|
||||
}
|
||||
|
||||
HashResult narHashAndSize = hashPath(htSHA256, actualPath);
|
||||
ValidPathInfo newInfo0 {
|
||||
finalPath,
|
||||
narHashAndSize.first,
|
||||
};
|
||||
|
||||
newInfo0.narSize = narHashAndSize.second;
|
||||
newInfo0.ca = FixedOutputHash {
|
||||
.method = outputHash.method,
|
||||
.hash = got,
|
||||
};
|
||||
newInfo0.references = refs.second;
|
||||
if (refs.first)
|
||||
newInfo0.references.insert(newInfo0.path);
|
||||
|
||||
assert(newInfo0.ca);
|
||||
return newInfo0;
|
||||
};
|
||||
|
@ -2428,6 +2468,10 @@ void LocalDerivationGoal::registerOutputs()
|
|||
},
|
||||
}, output.output);
|
||||
|
||||
/* FIXME: set proper permissions in restorePath() so
|
||||
we don't have to do another traversal. */
|
||||
canonicalisePathMetaData(actualPath, -1, inodesSeen);
|
||||
|
||||
/* Calculate where we'll move the output files. In the checking case we
|
||||
will leave leave them where they are, for now, rather than move to
|
||||
their usual "final destination" */
|
||||
|
@ -2460,6 +2504,7 @@ void LocalDerivationGoal::registerOutputs()
|
|||
assert(newInfo.ca);
|
||||
} else {
|
||||
auto destPath = worker.store.toRealPath(finalDestPath);
|
||||
deletePath(destPath);
|
||||
movePath(actualPath, destPath);
|
||||
actualPath = destPath;
|
||||
}
|
||||
|
@ -2615,12 +2660,21 @@ void LocalDerivationGoal::registerOutputs()
|
|||
but it's fine to do in all cases. */
|
||||
|
||||
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||
for (auto& [outputName, newInfo] : infos)
|
||||
worker.store.registerDrvOutput(Realisation{
|
||||
.id = DrvOutput{initialOutputs.at(outputName).outputHash, outputName},
|
||||
.outPath = newInfo.path});
|
||||
for (auto& [outputName, newInfo] : infos) {
|
||||
auto thisRealisation = Realisation{
|
||||
.id = DrvOutput{initialOutputs.at(outputName).outputHash,
|
||||
outputName},
|
||||
.outPath = newInfo.path};
|
||||
signRealisation(thisRealisation);
|
||||
worker.store.registerDrvOutput(thisRealisation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalDerivationGoal::signRealisation(Realisation & realisation)
|
||||
{
|
||||
getLocalStore().signRealisation(realisation);
|
||||
}
|
||||
|
||||
|
||||
void LocalDerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs)
|
||||
|
|
|
@ -108,6 +108,9 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
/* Paths that were added via recursive Nix calls. */
|
||||
StorePathSet addedPaths;
|
||||
|
||||
/* Realisations that were added via recursive Nix calls. */
|
||||
std::set<DrvOutput> addedDrvOutputs;
|
||||
|
||||
/* Recursive Nix calls are only allowed to build or realize paths
|
||||
in the original input closure or added via a recursive Nix call
|
||||
(so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
|
||||
|
@ -116,6 +119,12 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
{
|
||||
return inputPaths.count(path) || addedPaths.count(path);
|
||||
}
|
||||
bool isAllowed(const DrvOutput & id)
|
||||
{
|
||||
return addedDrvOutputs.count(id);
|
||||
}
|
||||
|
||||
bool isAllowed(const DerivedPath & req);
|
||||
|
||||
friend struct RestrictedStore;
|
||||
|
||||
|
@ -161,6 +170,8 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
as valid. */
|
||||
void registerOutputs() override;
|
||||
|
||||
void signRealisation(Realisation &) override;
|
||||
|
||||
/* Check that an output meets the requirements specified by the
|
||||
'outputChecks' attribute (or the legacy
|
||||
'{allowed,disallowed}{References,Requisites}' attributes). */
|
||||
|
|
|
@ -5,40 +5,32 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
SubstitutionGoal::SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||
PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||
: Goal(worker)
|
||||
, storePath(storePath)
|
||||
, repair(repair)
|
||||
, ca(ca)
|
||||
{
|
||||
state = &SubstitutionGoal::init;
|
||||
state = &PathSubstitutionGoal::init;
|
||||
name = fmt("substitution of '%s'", worker.store.printStorePath(this->storePath));
|
||||
trace("created");
|
||||
maintainExpectedSubstitutions = std::make_unique<MaintainCount<uint64_t>>(worker.expectedSubstitutions);
|
||||
}
|
||||
|
||||
|
||||
SubstitutionGoal::~SubstitutionGoal()
|
||||
PathSubstitutionGoal::~PathSubstitutionGoal()
|
||||
{
|
||||
try {
|
||||
if (thr.joinable()) {
|
||||
// FIXME: signal worker thread to quit.
|
||||
thr.join();
|
||||
worker.childTerminated(this);
|
||||
}
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::work()
|
||||
void PathSubstitutionGoal::work()
|
||||
{
|
||||
(this->*state)();
|
||||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::init()
|
||||
void PathSubstitutionGoal::init()
|
||||
{
|
||||
trace("init");
|
||||
|
||||
|
@ -59,10 +51,12 @@ void SubstitutionGoal::init()
|
|||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::tryNext()
|
||||
void PathSubstitutionGoal::tryNext()
|
||||
{
|
||||
trace("trying next substituter");
|
||||
|
||||
cleanup();
|
||||
|
||||
if (subs.size() == 0) {
|
||||
/* None left. Terminate this goal and let someone else deal
|
||||
with it. */
|
||||
|
@ -142,7 +136,7 @@ void SubstitutionGoal::tryNext()
|
|||
/* Bail out early if this substituter lacks a valid
|
||||
signature. LocalStore::addToStore() also checks for this, but
|
||||
only after we've downloaded the path. */
|
||||
if (!sub->isTrusted && worker.store.pathInfoIsTrusted(*info))
|
||||
if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info))
|
||||
{
|
||||
warn("substituter '%s' does not have a valid signature for path '%s'",
|
||||
sub->getUri(), worker.store.printStorePath(storePath));
|
||||
|
@ -154,16 +148,16 @@ void SubstitutionGoal::tryNext()
|
|||
paths referenced by this one. */
|
||||
for (auto & i : info->references)
|
||||
if (i != storePath) /* ignore self-references */
|
||||
addWaitee(worker.makeSubstitutionGoal(i));
|
||||
addWaitee(worker.makePathSubstitutionGoal(i));
|
||||
|
||||
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
||||
referencesValid();
|
||||
else
|
||||
state = &SubstitutionGoal::referencesValid;
|
||||
state = &PathSubstitutionGoal::referencesValid;
|
||||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::referencesValid()
|
||||
void PathSubstitutionGoal::referencesValid()
|
||||
{
|
||||
trace("all references realised");
|
||||
|
||||
|
@ -177,12 +171,12 @@ void SubstitutionGoal::referencesValid()
|
|||
if (i != storePath) /* ignore self-references */
|
||||
assert(worker.store.isValidPath(i));
|
||||
|
||||
state = &SubstitutionGoal::tryToRun;
|
||||
state = &PathSubstitutionGoal::tryToRun;
|
||||
worker.wakeUp(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::tryToRun()
|
||||
void PathSubstitutionGoal::tryToRun()
|
||||
{
|
||||
trace("trying to run");
|
||||
|
||||
|
@ -205,7 +199,7 @@ void SubstitutionGoal::tryToRun()
|
|||
thr = std::thread([this]() {
|
||||
try {
|
||||
/* Wake up the worker loop when we're done. */
|
||||
Finally updateStats([this]() { outPipe.writeSide = -1; });
|
||||
Finally updateStats([this]() { outPipe.writeSide.close(); });
|
||||
|
||||
Activity act(*logger, actSubstitute, Logger::Fields{worker.store.printStorePath(storePath), sub->getUri()});
|
||||
PushActivity pact(act.id);
|
||||
|
@ -221,11 +215,11 @@ void SubstitutionGoal::tryToRun()
|
|||
|
||||
worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false);
|
||||
|
||||
state = &SubstitutionGoal::finished;
|
||||
state = &PathSubstitutionGoal::finished;
|
||||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::finished()
|
||||
void PathSubstitutionGoal::finished()
|
||||
{
|
||||
trace("substitute finished");
|
||||
|
||||
|
@ -249,7 +243,7 @@ void SubstitutionGoal::finished()
|
|||
}
|
||||
|
||||
/* Try the next substitute. */
|
||||
state = &SubstitutionGoal::tryNext;
|
||||
state = &PathSubstitutionGoal::tryNext;
|
||||
worker.wakeUp(shared_from_this());
|
||||
return;
|
||||
}
|
||||
|
@ -278,14 +272,31 @@ void SubstitutionGoal::finished()
|
|||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::handleChildOutput(int fd, const string & data)
|
||||
void PathSubstitutionGoal::handleChildOutput(int fd, const string & data)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::handleEOF(int fd)
|
||||
void PathSubstitutionGoal::handleEOF(int fd)
|
||||
{
|
||||
if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
void PathSubstitutionGoal::cleanup()
|
||||
{
|
||||
try {
|
||||
if (thr.joinable()) {
|
||||
// FIXME: signal worker thread to quit.
|
||||
thr.join();
|
||||
worker.childTerminated(this);
|
||||
}
|
||||
|
||||
outPipe.close();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@ namespace nix {
|
|||
|
||||
class Worker;
|
||||
|
||||
struct SubstitutionGoal : public Goal
|
||||
struct PathSubstitutionGoal : public Goal
|
||||
{
|
||||
/* The store path that should be realised through a substitute. */
|
||||
StorePath storePath;
|
||||
|
||||
/* The path the substituter refers to the path as. This will be
|
||||
* different when the stores have different names. */
|
||||
different when the stores have different names. */
|
||||
std::optional<StorePath> subPath;
|
||||
|
||||
/* The remaining substituters. */
|
||||
|
@ -47,14 +47,15 @@ struct SubstitutionGoal : public Goal
|
|||
std::unique_ptr<MaintainCount<uint64_t>> maintainExpectedSubstitutions,
|
||||
maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload;
|
||||
|
||||
typedef void (SubstitutionGoal::*GoalState)();
|
||||
typedef void (PathSubstitutionGoal::*GoalState)();
|
||||
GoalState state;
|
||||
|
||||
/* Content address for recomputing store path */
|
||||
std::optional<ContentAddress> ca;
|
||||
|
||||
SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
~SubstitutionGoal();
|
||||
public:
|
||||
PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
~PathSubstitutionGoal();
|
||||
|
||||
void timedOut(Error && ex) override { abort(); };
|
||||
|
||||
|
@ -78,6 +79,8 @@ struct SubstitutionGoal : public Goal
|
|||
/* Callback used by the worker to write to the log. */
|
||||
void handleChildOutput(int fd, const string & data) override;
|
||||
void handleEOF(int fd) override;
|
||||
|
||||
void cleanup() override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "machines.hh"
|
||||
#include "worker.hh"
|
||||
#include "substitution-goal.hh"
|
||||
#include "drv-output-substitution-goal.hh"
|
||||
#include "local-derivation-goal.hh"
|
||||
#include "hook-instance.hh"
|
||||
|
||||
|
@ -78,20 +79,32 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath
|
|||
}
|
||||
|
||||
|
||||
std::shared_ptr<SubstitutionGoal> Worker::makeSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||
std::shared_ptr<PathSubstitutionGoal> Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||
{
|
||||
std::weak_ptr<SubstitutionGoal> & goal_weak = substitutionGoals[path];
|
||||
std::weak_ptr<PathSubstitutionGoal> & goal_weak = substitutionGoals[path];
|
||||
auto goal = goal_weak.lock(); // FIXME
|
||||
if (!goal) {
|
||||
goal = std::make_shared<SubstitutionGoal>(path, *this, repair, ca);
|
||||
goal = std::make_shared<PathSubstitutionGoal>(path, *this, repair, ca);
|
||||
goal_weak = goal;
|
||||
wakeUp(goal);
|
||||
}
|
||||
return goal;
|
||||
}
|
||||
|
||||
template<typename G>
|
||||
static void removeGoal(std::shared_ptr<G> goal, std::map<StorePath, std::weak_ptr<G>> & goalMap)
|
||||
std::shared_ptr<DrvOutputSubstitutionGoal> Worker::makeDrvOutputSubstitutionGoal(const DrvOutput& id, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||
{
|
||||
std::weak_ptr<DrvOutputSubstitutionGoal> & goal_weak = drvOutputSubstitutionGoals[id];
|
||||
auto goal = goal_weak.lock(); // FIXME
|
||||
if (!goal) {
|
||||
goal = std::make_shared<DrvOutputSubstitutionGoal>(id, *this, repair, ca);
|
||||
goal_weak = goal;
|
||||
wakeUp(goal);
|
||||
}
|
||||
return goal;
|
||||
}
|
||||
|
||||
template<typename K, typename G>
|
||||
static void removeGoal(std::shared_ptr<G> goal, std::map<K, std::weak_ptr<G>> & goalMap)
|
||||
{
|
||||
/* !!! inefficient */
|
||||
for (auto i = goalMap.begin();
|
||||
|
@ -109,10 +122,13 @@ void Worker::removeGoal(GoalPtr goal)
|
|||
{
|
||||
if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
||||
nix::removeGoal(drvGoal, derivationGoals);
|
||||
else if (auto subGoal = std::dynamic_pointer_cast<SubstitutionGoal>(goal))
|
||||
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
||||
nix::removeGoal(subGoal, substitutionGoals);
|
||||
else if (auto subGoal = std::dynamic_pointer_cast<DrvOutputSubstitutionGoal>(goal))
|
||||
nix::removeGoal(subGoal, drvOutputSubstitutionGoals);
|
||||
else
|
||||
assert(false);
|
||||
|
||||
if (topGoals.find(goal) != topGoals.end()) {
|
||||
topGoals.erase(goal);
|
||||
/* If a top-level goal failed, then kill all other goals
|
||||
|
@ -211,14 +227,14 @@ void Worker::waitForAWhile(GoalPtr goal)
|
|||
|
||||
void Worker::run(const Goals & _topGoals)
|
||||
{
|
||||
std::vector<nix::StorePathWithOutputs> topPaths;
|
||||
std::vector<nix::DerivedPath> topPaths;
|
||||
|
||||
for (auto & i : _topGoals) {
|
||||
topGoals.insert(i);
|
||||
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) {
|
||||
topPaths.push_back({goal->drvPath, goal->wantedOutputs});
|
||||
} else if (auto goal = dynamic_cast<SubstitutionGoal *>(i.get())) {
|
||||
topPaths.push_back({goal->storePath});
|
||||
topPaths.push_back(DerivedPath::Built{goal->drvPath, goal->wantedOutputs});
|
||||
} else if (auto goal = dynamic_cast<PathSubstitutionGoal *>(i.get())) {
|
||||
topPaths.push_back(DerivedPath::Opaque{goal->storePath});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,7 +487,10 @@ void Worker::markContentsGood(const StorePath & path)
|
|||
}
|
||||
|
||||
|
||||
GoalPtr upcast_goal(std::shared_ptr<SubstitutionGoal> subGoal) {
|
||||
GoalPtr upcast_goal(std::shared_ptr<PathSubstitutionGoal> subGoal) {
|
||||
return subGoal;
|
||||
}
|
||||
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> subGoal) {
|
||||
return subGoal;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "lock.hh"
|
||||
#include "store-api.hh"
|
||||
#include "goal.hh"
|
||||
#include "realisation.hh"
|
||||
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
@ -12,18 +13,20 @@ namespace nix {
|
|||
|
||||
/* Forward definition. */
|
||||
struct DerivationGoal;
|
||||
struct SubstitutionGoal;
|
||||
struct PathSubstitutionGoal;
|
||||
class DrvOutputSubstitutionGoal;
|
||||
|
||||
/* Workaround for not being able to declare a something like
|
||||
|
||||
class SubstitutionGoal : public Goal;
|
||||
class PathSubstitutionGoal : public Goal;
|
||||
|
||||
even when Goal is a complete type.
|
||||
|
||||
This is still a static cast. The purpose of exporting it is to define it in
|
||||
a place where `SubstitutionGoal` is concrete, and use it in a place where it
|
||||
a place where `PathSubstitutionGoal` is concrete, and use it in a place where it
|
||||
is opaque. */
|
||||
GoalPtr upcast_goal(std::shared_ptr<SubstitutionGoal> subGoal);
|
||||
GoalPtr upcast_goal(std::shared_ptr<PathSubstitutionGoal> subGoal);
|
||||
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> subGoal);
|
||||
|
||||
typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
|
||||
|
||||
|
@ -72,7 +75,8 @@ private:
|
|||
/* Maps used to prevent multiple instantiations of a goal for the
|
||||
same derivation / path. */
|
||||
std::map<StorePath, std::weak_ptr<DerivationGoal>> derivationGoals;
|
||||
std::map<StorePath, std::weak_ptr<SubstitutionGoal>> substitutionGoals;
|
||||
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
||||
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
||||
|
||||
/* Goals waiting for busy paths to be unlocked. */
|
||||
WeakGoals waitingForAnyGoal;
|
||||
|
@ -146,7 +150,8 @@ public:
|
|||
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||
|
||||
/* substitution goal */
|
||||
std::shared_ptr<SubstitutionGoal> makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
std::shared_ptr<PathSubstitutionGoal> makePathSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
std::shared_ptr<DrvOutputSubstitutionGoal> makeDrvOutputSubstitutionGoal(const DrvOutput & id, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
|
||||
/* Remove a dead goal. */
|
||||
void removeGoal(GoalPtr goal);
|
||||
|
|
|
@ -3,9 +3,19 @@
|
|||
-- is enabled
|
||||
|
||||
create table if not exists Realisations (
|
||||
id integer primary key autoincrement not null,
|
||||
drvPath text not null,
|
||||
outputName text not null, -- symbolic output id, usually "out"
|
||||
outputPath integer not null,
|
||||
primary key (drvPath, outputName),
|
||||
signatures text, -- space-separated list
|
||||
foreign key (outputPath) references ValidPaths(id) on delete cascade
|
||||
);
|
||||
|
||||
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||
|
||||
create table if not exists RealisationsRefs (
|
||||
referrer integer not null,
|
||||
realisationReference integer,
|
||||
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "monitor-fd.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "store-api.hh"
|
||||
#include "path-with-outputs.hh"
|
||||
#include "finally.hh"
|
||||
#include "affinity.hh"
|
||||
#include "archive.hh"
|
||||
|
@ -259,6 +260,18 @@ static void writeValidPathInfo(
|
|||
}
|
||||
}
|
||||
|
||||
static std::vector<DerivedPath> readDerivedPaths(Store & store, unsigned int clientVersion, Source & from)
|
||||
{
|
||||
std::vector<DerivedPath> reqs;
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 30) {
|
||||
reqs = worker_proto::read(store, from, Phantom<std::vector<DerivedPath>> {});
|
||||
} else {
|
||||
for (auto & s : readStrings<Strings>(from))
|
||||
reqs.push_back(parsePathWithOutputs(store, s).toDerivedPath());
|
||||
}
|
||||
return reqs;
|
||||
}
|
||||
|
||||
static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
TrustedFlag trusted, RecursiveFlag recursive, unsigned int clientVersion,
|
||||
Source & from, BufferedSink & to, unsigned int op)
|
||||
|
@ -493,9 +506,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
|||
}
|
||||
|
||||
case wopBuildPaths: {
|
||||
std::vector<StorePathWithOutputs> drvs;
|
||||
for (auto & s : readStrings<Strings>(from))
|
||||
drvs.push_back(store->parsePathWithOutputs(s));
|
||||
auto drvs = readDerivedPaths(*store, clientVersion, from);
|
||||
BuildMode mode = bmNormal;
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 15) {
|
||||
mode = (BuildMode) readInt(from);
|
||||
|
@ -575,7 +586,10 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
|||
auto res = store->buildDerivation(drvPath, drv, buildMode);
|
||||
logger->stopWork();
|
||||
to << res.status << res.errorMsg;
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 0xc) {
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 29) {
|
||||
to << res.timesBuilt << res.isNonDeterministic << res.startTime << res.stopTime;
|
||||
}
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 28) {
|
||||
worker_proto::write(*store, to, res.builtOutputs);
|
||||
}
|
||||
break;
|
||||
|
@ -856,9 +870,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
|||
}
|
||||
|
||||
case wopQueryMissing: {
|
||||
std::vector<StorePathWithOutputs> targets;
|
||||
for (auto & s : readStrings<Strings>(from))
|
||||
targets.push_back(store->parsePathWithOutputs(s));
|
||||
auto targets = readDerivedPaths(*store, clientVersion, from);
|
||||
logger->startWork();
|
||||
StorePathSet willBuild, willSubstitute, unknown;
|
||||
uint64_t downloadSize, narSize;
|
||||
|
@ -873,11 +885,15 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
|||
|
||||
case wopRegisterDrvOutput: {
|
||||
logger->startWork();
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) < 31) {
|
||||
auto outputId = DrvOutput::parse(readString(from));
|
||||
auto outputPath = StorePath(readString(from));
|
||||
auto resolvedDrv = StorePath(readString(from));
|
||||
store->registerDrvOutput(Realisation{
|
||||
.id = outputId, .outPath = outputPath});
|
||||
} else {
|
||||
auto realisation = worker_proto::read(*store, from, Phantom<Realisation>());
|
||||
store->registerDrvOutput(realisation);
|
||||
}
|
||||
logger->stopWork();
|
||||
break;
|
||||
}
|
||||
|
@ -887,9 +903,15 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
|||
auto outputId = DrvOutput::parse(readString(from));
|
||||
auto info = store->queryRealisation(outputId);
|
||||
logger->stopWork();
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) < 31) {
|
||||
std::set<StorePath> outPaths;
|
||||
if (info) outPaths.insert(info->outPath);
|
||||
worker_proto::write(*store, to, outPaths);
|
||||
} else {
|
||||
std::set<Realisation> realisations;
|
||||
if (info) realisations.insert(*info);
|
||||
worker_proto::write(*store, to, realisations);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -590,14 +590,6 @@ std::map<std::string, Hash> staticOutputHashes(Store& store, const Derivation& d
|
|||
}
|
||||
|
||||
|
||||
std::string StorePathWithOutputs::to_string(const Store & store) const
|
||||
{
|
||||
return outputs.empty()
|
||||
? store.printStorePath(path)
|
||||
: store.printStorePath(path) + "!" + concatStringsSep(",", outputs);
|
||||
}
|
||||
|
||||
|
||||
bool wantOutput(const string & output, const std::set<string> & wanted)
|
||||
{
|
||||
return wanted.empty() || wanted.find(output) != wanted.end();
|
||||
|
|
|
@ -52,7 +52,7 @@ struct DerivationOutput
|
|||
DerivationOutputCAFloating,
|
||||
DerivationOutputDeferred
|
||||
> output;
|
||||
std::optional<HashType> hashAlgoOpt(const Store & store) const;
|
||||
|
||||
/* Note, when you use this function you should make sure that you're passing
|
||||
the right derivation name. When in doubt, you should use the safer
|
||||
interface provided by BasicDerivation::outputsAndOptPaths */
|
||||
|
|
118
src/libstore/derived-path.cc
Normal file
118
src/libstore/derived-path.cc
Normal file
|
@ -0,0 +1,118 @@
|
|||
#include "derived-path.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
nlohmann::json DerivedPath::Opaque::toJSON(ref<Store> store) const {
|
||||
nlohmann::json res;
|
||||
res["path"] = store->printStorePath(path);
|
||||
return res;
|
||||
}
|
||||
|
||||
nlohmann::json BuiltPath::Built::toJSON(ref<Store> store) const {
|
||||
nlohmann::json res;
|
||||
res["drvPath"] = store->printStorePath(drvPath);
|
||||
for (const auto& [output, path] : outputs) {
|
||||
res["outputs"][output] = store->printStorePath(path);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
StorePathSet BuiltPath::outPaths() const
|
||||
{
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[](BuiltPath::Opaque p) { return StorePathSet{p.path}; },
|
||||
[](BuiltPath::Built b) {
|
||||
StorePathSet res;
|
||||
for (auto & [_, path] : b.outputs)
|
||||
res.insert(path);
|
||||
return res;
|
||||
},
|
||||
}, raw()
|
||||
);
|
||||
}
|
||||
|
||||
nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store) {
|
||||
auto res = nlohmann::json::array();
|
||||
for (const BuiltPath & buildable : buildables) {
|
||||
std::visit([&res, store](const auto & buildable) {
|
||||
res.push_back(buildable.toJSON(store));
|
||||
}, buildable.raw());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::string DerivedPath::Opaque::to_string(const Store & store) const {
|
||||
return store.printStorePath(path);
|
||||
}
|
||||
|
||||
std::string DerivedPath::Built::to_string(const Store & store) const {
|
||||
return store.printStorePath(drvPath)
|
||||
+ "!"
|
||||
+ (outputs.empty() ? std::string { "*" } : concatStringsSep(",", outputs));
|
||||
}
|
||||
|
||||
std::string DerivedPath::to_string(const Store & store) const
|
||||
{
|
||||
return std::visit(
|
||||
[&](const auto & req) { return req.to_string(store); },
|
||||
this->raw());
|
||||
}
|
||||
|
||||
|
||||
DerivedPath::Opaque DerivedPath::Opaque::parse(const Store & store, std::string_view s)
|
||||
{
|
||||
return {store.parseStorePath(s)};
|
||||
}
|
||||
|
||||
DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_view s)
|
||||
{
|
||||
size_t n = s.find("!");
|
||||
assert(n != s.npos);
|
||||
auto drvPath = store.parseStorePath(s.substr(0, n));
|
||||
auto outputsS = s.substr(n + 1);
|
||||
std::set<string> outputs;
|
||||
if (outputsS != "*")
|
||||
outputs = tokenizeString<std::set<string>>(outputsS, ",");
|
||||
return {drvPath, outputs};
|
||||
}
|
||||
|
||||
DerivedPath DerivedPath::parse(const Store & store, std::string_view s)
|
||||
{
|
||||
size_t n = s.find("!");
|
||||
return n == s.npos
|
||||
? (DerivedPath) DerivedPath::Opaque::parse(store, s)
|
||||
: (DerivedPath) DerivedPath::Built::parse(store, s);
|
||||
}
|
||||
|
||||
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
||||
{
|
||||
RealisedPath::Set res;
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](BuiltPath::Opaque p) { res.insert(p.path); },
|
||||
[&](BuiltPath::Built p) {
|
||||
auto drvHashes =
|
||||
staticOutputHashes(store, store.readDerivation(p.drvPath));
|
||||
for (auto& [outputName, outputPath] : p.outputs) {
|
||||
if (settings.isExperimentalFeatureEnabled(
|
||||
"ca-derivations")) {
|
||||
auto thisRealisation = store.queryRealisation(
|
||||
DrvOutput{drvHashes.at(outputName), outputName});
|
||||
assert(thisRealisation); // We’ve built it, so we must h
|
||||
// ve the realisation
|
||||
res.insert(*thisRealisation);
|
||||
} else {
|
||||
res.insert(outputPath);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
raw());
|
||||
return res;
|
||||
}
|
||||
}
|
123
src/libstore/derived-path.hh
Normal file
123
src/libstore/derived-path.hh
Normal file
|
@ -0,0 +1,123 @@
|
|||
#pragma once
|
||||
|
||||
#include "util.hh"
|
||||
#include "path.hh"
|
||||
#include "realisation.hh"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
class Store;
|
||||
|
||||
/**
|
||||
* An opaque derived path.
|
||||
*
|
||||
* Opaque derived paths are just store paths, and fully evaluated. They
|
||||
* cannot be simplified further. Since they are opaque, they cannot be
|
||||
* built, but they can fetched.
|
||||
*/
|
||||
struct DerivedPathOpaque {
|
||||
StorePath path;
|
||||
|
||||
nlohmann::json toJSON(ref<Store> store) const;
|
||||
std::string to_string(const Store & store) const;
|
||||
static DerivedPathOpaque parse(const Store & store, std::string_view);
|
||||
};
|
||||
|
||||
/**
|
||||
* A derived path that is built from a derivation
|
||||
*
|
||||
* Built derived paths are pair of a derivation and some output names.
|
||||
* They are evaluated by building the derivation, and then replacing the
|
||||
* output names with the resulting outputs.
|
||||
*
|
||||
* Note that does mean a derived store paths evaluates to multiple
|
||||
* opaque paths, which is sort of icky as expressions are supposed to
|
||||
* evaluate to single values. Perhaps this should have just a single
|
||||
* output name.
|
||||
*/
|
||||
struct DerivedPathBuilt {
|
||||
StorePath drvPath;
|
||||
std::set<std::string> outputs;
|
||||
|
||||
std::string to_string(const Store & store) const;
|
||||
static DerivedPathBuilt parse(const Store & store, std::string_view);
|
||||
};
|
||||
|
||||
using _DerivedPathRaw = std::variant<
|
||||
DerivedPathOpaque,
|
||||
DerivedPathBuilt
|
||||
>;
|
||||
|
||||
/**
|
||||
* A "derived path" is a very simple sort of expression that evaluates
|
||||
* to (concrete) store path. It is either:
|
||||
*
|
||||
* - opaque, in which case it is just a concrete store path with
|
||||
* possibly no known derivation
|
||||
*
|
||||
* - built, in which case it is a pair of a derivation path and an
|
||||
* output name.
|
||||
*/
|
||||
struct DerivedPath : _DerivedPathRaw {
|
||||
using Raw = _DerivedPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
using Built = DerivedPathBuilt;
|
||||
|
||||
inline const Raw & raw() const {
|
||||
return static_cast<const Raw &>(*this);
|
||||
}
|
||||
|
||||
std::string to_string(const Store & store) const;
|
||||
static DerivedPath parse(const Store & store, std::string_view);
|
||||
};
|
||||
|
||||
/**
|
||||
* A built derived path with hints in the form of optional concrete output paths.
|
||||
*
|
||||
* See 'BuiltPath' for more an explanation.
|
||||
*/
|
||||
struct BuiltPathBuilt {
|
||||
StorePath drvPath;
|
||||
std::map<std::string, StorePath> outputs;
|
||||
|
||||
nlohmann::json toJSON(ref<Store> store) const;
|
||||
static BuiltPathBuilt parse(const Store & store, std::string_view);
|
||||
};
|
||||
|
||||
using _BuiltPathRaw = std::variant<
|
||||
DerivedPath::Opaque,
|
||||
BuiltPathBuilt
|
||||
>;
|
||||
|
||||
/**
|
||||
* A built path. Similar to a `DerivedPath`, but enriched with the corresponding
|
||||
* output path(s).
|
||||
*/
|
||||
struct BuiltPath : _BuiltPathRaw {
|
||||
using Raw = _BuiltPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
using Built = BuiltPathBuilt;
|
||||
|
||||
inline const Raw & raw() const {
|
||||
return static_cast<const Raw &>(*this);
|
||||
}
|
||||
|
||||
StorePathSet outPaths() const;
|
||||
RealisedPath::Set toRealisedPaths(Store & store) const;
|
||||
|
||||
};
|
||||
|
||||
typedef std::vector<DerivedPath> DerivedPaths;
|
||||
typedef std::vector<BuiltPath> BuiltPaths;
|
||||
|
||||
nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store);
|
||||
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
#include "finally.hh"
|
||||
#include "callback.hh"
|
||||
|
||||
#ifdef ENABLE_S3
|
||||
#if ENABLE_S3
|
||||
#include <aws/core/client/ClientConfiguration.h>
|
||||
#endif
|
||||
|
||||
|
@ -148,7 +148,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
}
|
||||
|
||||
LambdaSink finalSink;
|
||||
std::shared_ptr<CompressionSink> decompressionSink;
|
||||
std::shared_ptr<FinishSink> decompressionSink;
|
||||
std::optional<StringSink> errorSink;
|
||||
|
||||
std::exception_ptr writeException;
|
||||
|
@ -665,7 +665,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
writeFull(wakeupPipe.writeSide.get(), " ");
|
||||
}
|
||||
|
||||
#ifdef ENABLE_S3
|
||||
#if ENABLE_S3
|
||||
std::tuple<std::string, std::string, Store::Params> parseS3Uri(std::string uri)
|
||||
{
|
||||
auto [path, params] = splitUriAndParams(uri);
|
||||
|
@ -688,7 +688,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
if (hasPrefix(request.uri, "s3://")) {
|
||||
// FIXME: do this on a worker thread
|
||||
try {
|
||||
#ifdef ENABLE_S3
|
||||
#if ENABLE_S3
|
||||
auto [bucketName, key, params] = parseS3Uri(request.uri);
|
||||
|
||||
std::string profile = get(params, "profile").value_or("");
|
||||
|
|
|
@ -775,7 +775,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
|||
|
||||
try {
|
||||
|
||||
AutoCloseDir dir(opendir(realStoreDir.c_str()));
|
||||
AutoCloseDir dir(opendir(realStoreDir.get().c_str()));
|
||||
if (!dir) throw SysError("opening directory '%1%'", realStoreDir);
|
||||
|
||||
/* Read the store and immediately delete all paths that
|
||||
|
@ -856,7 +856,7 @@ void LocalStore::autoGC(bool sync)
|
|||
return std::stoll(readFile(*fakeFreeSpaceFile));
|
||||
|
||||
struct statvfs st;
|
||||
if (statvfs(realStoreDir.c_str(), &st))
|
||||
if (statvfs(realStoreDir.get().c_str(), &st))
|
||||
throw SysError("getting filesystem info about '%s'", realStoreDir);
|
||||
|
||||
return (uint64_t) st.f_bavail * st.f_frsize;
|
||||
|
|
|
@ -81,7 +81,7 @@ void loadConfFile()
|
|||
|
||||
/* We only want to send overrides to the daemon, i.e. stuff from
|
||||
~/.nix/nix.conf or the command line. */
|
||||
globalConfig.resetOverriden();
|
||||
globalConfig.resetOverridden();
|
||||
|
||||
auto files = settings.nixUserConfFiles;
|
||||
for (auto file = files.rbegin(); file != files.rend(); file++) {
|
||||
|
|
|
@ -206,7 +206,10 @@ public:
|
|||
|
||||
Setting<std::string> builders{
|
||||
this, "@" + nixConfDir + "/machines", "builders",
|
||||
"A semicolon-separated list of build machines, in the format of `nix.machines`."};
|
||||
R"(
|
||||
A semicolon-separated list of build machines.
|
||||
For the exact format and examples, see [the manual chapter on remote builds](../advanced-topics/distributed-builds.md)
|
||||
)"};
|
||||
|
||||
Setting<bool> buildersUseSubstitutes{
|
||||
this, false, "builders-use-substitutes",
|
||||
|
@ -614,8 +617,10 @@ public:
|
|||
Strings{"https://cache.nixos.org/"},
|
||||
"substituters",
|
||||
R"(
|
||||
A list of URLs of substituters, separated by whitespace. The default
|
||||
is `https://cache.nixos.org`.
|
||||
A list of URLs of substituters, separated by whitespace. Substituters
|
||||
are tried based on their Priority value, which each substituter can set
|
||||
independently. Lower value means higher priority.
|
||||
The default is `https://cache.nixos.org`, with a Priority of 40.
|
||||
)",
|
||||
{"binary-caches"}};
|
||||
|
||||
|
@ -698,7 +703,7 @@ public:
|
|||
send a series of commands to modify various settings to stdout. The
|
||||
currently recognized commands are:
|
||||
|
||||
- `extra-sandbox-paths`
|
||||
- `extra-sandbox-paths`\
|
||||
Pass a list of files and directories to be included in the
|
||||
sandbox for this build. One entry per line, terminated by an
|
||||
empty line. Entries have the same format as `sandbox-paths`.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "remote-store.hh"
|
||||
#include "serve-protocol.hh"
|
||||
#include "store-api.hh"
|
||||
#include "path-with-outputs.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "ssh.hh"
|
||||
#include "derivations.hh"
|
||||
|
@ -266,14 +267,23 @@ public:
|
|||
return status;
|
||||
}
|
||||
|
||||
void buildPaths(const std::vector<StorePathWithOutputs> & drvPaths, BuildMode buildMode) override
|
||||
void buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode) override
|
||||
{
|
||||
auto conn(connections->get());
|
||||
|
||||
conn->to << cmdBuildPaths;
|
||||
Strings ss;
|
||||
for (auto & p : drvPaths)
|
||||
ss.push_back(p.to_string(*this));
|
||||
for (auto & p : drvPaths) {
|
||||
auto sOrDrvPath = StorePathWithOutputs::tryFromDerivedPath(p);
|
||||
std::visit(overloaded {
|
||||
[&](StorePathWithOutputs s) {
|
||||
ss.push_back(s.to_string(*this));
|
||||
},
|
||||
[&](StorePath drvPath) {
|
||||
throw Error("wanted to fetch '%s' but the legacy ssh protocol doesn't support merely substituting drv files via the build paths command. It would build them instead. Try using ssh-ng://", printStorePath(drvPath));
|
||||
},
|
||||
}, sOrDrvPath);
|
||||
}
|
||||
conn->to << ss;
|
||||
|
||||
putBuildSettings(*conn);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include "globals.hh"
|
||||
#include "nar-info-disk-cache.hh"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
|
||||
|
@ -50,7 +52,8 @@ protected:
|
|||
const std::string & mimeType) override
|
||||
{
|
||||
auto path2 = binaryCacheDir + "/" + path;
|
||||
Path tmp = path2 + ".tmp." + std::to_string(getpid());
|
||||
static std::atomic<int> counter{0};
|
||||
Path tmp = fmt("%s.tmp.%d.%d", path2, getpid(), ++counter);
|
||||
AutoDelete del(tmp, false);
|
||||
StreamToSourceAdapter source(istream);
|
||||
writeFile(tmp, source);
|
||||
|
@ -90,7 +93,7 @@ protected:
|
|||
void LocalBinaryCacheStore::init()
|
||||
{
|
||||
createDirs(binaryCacheDir + "/nar");
|
||||
createDirs(binaryCacheDir + realisationsPrefix);
|
||||
createDirs(binaryCacheDir + "/" + realisationsPrefix);
|
||||
if (writeDebugInfo)
|
||||
createDirs(binaryCacheDir + "/debuginfo");
|
||||
BinaryCacheStore::init();
|
||||
|
|
|
@ -18,6 +18,9 @@ struct LocalFSStoreConfig : virtual StoreConfig
|
|||
const PathSetting logDir{(StoreConfig*) this, false,
|
||||
rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir,
|
||||
"log", "directory where Nix will store state"};
|
||||
const PathSetting realStoreDir{(StoreConfig*) this, false,
|
||||
rootDir != "" ? rootDir + "/nix/store" : storeDir, "real",
|
||||
"physical path to the Nix store"};
|
||||
};
|
||||
|
||||
class LocalFSStore : public virtual LocalFSStoreConfig, public virtual Store
|
||||
|
@ -34,7 +37,7 @@ public:
|
|||
/* Register a permanent GC root. */
|
||||
Path addPermRoot(const StorePath & storePath, const Path & gcRoot);
|
||||
|
||||
virtual Path getRealStoreDir() { return storeDir; }
|
||||
virtual Path getRealStoreDir() { return realStoreDir; }
|
||||
|
||||
Path toRealPath(const Path & storePath) override
|
||||
{
|
||||
|
|
|
@ -53,12 +53,15 @@ struct LocalStore::State::Stmts {
|
|||
SQLiteStmt InvalidatePath;
|
||||
SQLiteStmt AddDerivationOutput;
|
||||
SQLiteStmt RegisterRealisedOutput;
|
||||
SQLiteStmt UpdateRealisedOutput;
|
||||
SQLiteStmt QueryValidDerivers;
|
||||
SQLiteStmt QueryDerivationOutputs;
|
||||
SQLiteStmt QueryRealisedOutput;
|
||||
SQLiteStmt QueryAllRealisedOutputs;
|
||||
SQLiteStmt QueryPathFromHashPart;
|
||||
SQLiteStmt QueryValidPaths;
|
||||
SQLiteStmt QueryRealisationReferences;
|
||||
SQLiteStmt AddRealisationReference;
|
||||
};
|
||||
|
||||
int getSchema(Path schemaPath)
|
||||
|
@ -76,7 +79,7 @@ int getSchema(Path schemaPath)
|
|||
|
||||
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||
{
|
||||
const int nixCASchemaVersion = 1;
|
||||
const int nixCASchemaVersion = 2;
|
||||
int curCASchema = getSchema(schemaPath);
|
||||
if (curCASchema != nixCASchemaVersion) {
|
||||
if (curCASchema > nixCASchemaVersion) {
|
||||
|
@ -94,7 +97,39 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
|||
#include "ca-specific-schema.sql.gen.hh"
|
||||
;
|
||||
db.exec(schema);
|
||||
curCASchema = nixCASchemaVersion;
|
||||
}
|
||||
|
||||
if (curCASchema < 2) {
|
||||
SQLiteTxn txn(db);
|
||||
// Ugly little sql dance to add a new `id` column and make it the primary key
|
||||
db.exec(R"(
|
||||
create table Realisations2 (
|
||||
id integer primary key autoincrement not null,
|
||||
drvPath text not null,
|
||||
outputName text not null, -- symbolic output id, usually "out"
|
||||
outputPath integer not null,
|
||||
signatures text, -- space-separated list
|
||||
foreign key (outputPath) references ValidPaths(id) on delete cascade
|
||||
);
|
||||
insert into Realisations2 (drvPath, outputName, outputPath, signatures)
|
||||
select drvPath, outputName, outputPath, signatures from Realisations;
|
||||
drop table Realisations;
|
||||
alter table Realisations2 rename to Realisations;
|
||||
)");
|
||||
db.exec(R"(
|
||||
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||
|
||||
create table if not exists RealisationsRefs (
|
||||
referrer integer not null,
|
||||
realisationReference integer,
|
||||
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||
);
|
||||
)");
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
||||
lockFile(lockFd.get(), ltRead, true);
|
||||
}
|
||||
|
@ -106,9 +141,6 @@ LocalStore::LocalStore(const Params & params)
|
|||
, LocalStoreConfig(params)
|
||||
, Store(params)
|
||||
, LocalFSStore(params)
|
||||
, realStoreDir_{this, false, rootDir != "" ? rootDir + "/nix/store" : storeDir, "real",
|
||||
"physical path to the Nix store"}
|
||||
, realStoreDir(realStoreDir_)
|
||||
, dbDir(stateDir + "/db")
|
||||
, linksDir(realStoreDir + "/.links")
|
||||
, reservedPath(dbDir + "/reserved")
|
||||
|
@ -153,13 +185,13 @@ LocalStore::LocalStore(const Params & params)
|
|||
printError("warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
|
||||
else {
|
||||
struct stat st;
|
||||
if (stat(realStoreDir.c_str(), &st))
|
||||
if (stat(realStoreDir.get().c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", realStoreDir);
|
||||
|
||||
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
|
||||
if (chown(realStoreDir.c_str(), 0, gr->gr_gid) == -1)
|
||||
if (chown(realStoreDir.get().c_str(), 0, gr->gr_gid) == -1)
|
||||
throw SysError("changing ownership of path '%1%'", realStoreDir);
|
||||
if (chmod(realStoreDir.c_str(), perm) == -1)
|
||||
if (chmod(realStoreDir.get().c_str(), perm) == -1)
|
||||
throw SysError("changing permissions on path '%1%'", realStoreDir);
|
||||
}
|
||||
}
|
||||
|
@ -310,13 +342,22 @@ LocalStore::LocalStore(const Params & params)
|
|||
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||
state->stmts->RegisterRealisedOutput.create(state->db,
|
||||
R"(
|
||||
insert or replace into Realisations (drvPath, outputName, outputPath)
|
||||
values (?, ?, (select id from ValidPaths where path = ?))
|
||||
insert or replace into Realisations (drvPath, outputName, outputPath, signatures)
|
||||
values (?, ?, (select id from ValidPaths where path = ?), ?)
|
||||
;
|
||||
)");
|
||||
state->stmts->UpdateRealisedOutput.create(state->db,
|
||||
R"(
|
||||
update Realisations
|
||||
set signatures = ?
|
||||
where
|
||||
drvPath = ? and
|
||||
outputName = ?
|
||||
;
|
||||
)");
|
||||
state->stmts->QueryRealisedOutput.create(state->db,
|
||||
R"(
|
||||
select Output.path from Realisations
|
||||
select Realisations.id, Output.path, Realisations.signatures from Realisations
|
||||
inner join ValidPaths as Output on Output.id = Realisations.outputPath
|
||||
where drvPath = ? and outputName = ?
|
||||
;
|
||||
|
@ -328,6 +369,19 @@ LocalStore::LocalStore(const Params & params)
|
|||
where drvPath = ?
|
||||
;
|
||||
)");
|
||||
state->stmts->QueryRealisationReferences.create(state->db,
|
||||
R"(
|
||||
select drvPath, outputName from Realisations
|
||||
join RealisationsRefs on realisationReference = Realisations.id
|
||||
where referrer = ?;
|
||||
)");
|
||||
state->stmts->AddRealisationReference.create(state->db,
|
||||
R"(
|
||||
insert or replace into RealisationsRefs (referrer, realisationReference)
|
||||
values (
|
||||
?,
|
||||
(select id from Realisations where drvPath = ? and outputName = ?));
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -437,14 +491,14 @@ void LocalStore::makeStoreWritable()
|
|||
if (getuid() != 0) return;
|
||||
/* Check if /nix/store is on a read-only mount. */
|
||||
struct statvfs stat;
|
||||
if (statvfs(realStoreDir.c_str(), &stat) != 0)
|
||||
if (statvfs(realStoreDir.get().c_str(), &stat) != 0)
|
||||
throw SysError("getting info about the Nix store mount point");
|
||||
|
||||
if (stat.f_flag & ST_RDONLY) {
|
||||
if (unshare(CLONE_NEWNS) == -1)
|
||||
throw SysError("setting up a private mount namespace");
|
||||
|
||||
if (mount(0, realStoreDir.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
|
||||
if (mount(0, realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
|
||||
throw SysError("remounting %1% writable", realStoreDir);
|
||||
}
|
||||
#endif
|
||||
|
@ -652,17 +706,66 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
|
|||
}
|
||||
}
|
||||
|
||||
void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs)
|
||||
{
|
||||
settings.requireExperimentalFeature("ca-derivations");
|
||||
if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info))
|
||||
registerDrvOutput(info);
|
||||
else
|
||||
throw Error("cannot register realisation '%s' because it lacks a valid signature", info.outPath.to_string());
|
||||
}
|
||||
|
||||
void LocalStore::registerDrvOutput(const Realisation & info)
|
||||
{
|
||||
settings.requireExperimentalFeature("ca-derivations");
|
||||
auto state(_state.lock());
|
||||
retrySQLite<void>([&]() {
|
||||
auto state(_state.lock());
|
||||
if (auto oldR = queryRealisation_(*state, info.id)) {
|
||||
if (info.isCompatibleWith(*oldR)) {
|
||||
auto combinedSignatures = oldR->signatures;
|
||||
combinedSignatures.insert(info.signatures.begin(),
|
||||
info.signatures.end());
|
||||
state->stmts->UpdateRealisedOutput.use()
|
||||
(concatStringsSep(" ", combinedSignatures))
|
||||
(info.id.strHash())
|
||||
(info.id.outputName)
|
||||
.exec();
|
||||
} else {
|
||||
throw Error("Trying to register a realisation of '%s', but we already "
|
||||
"have another one locally.\n"
|
||||
"Local: %s\n"
|
||||
"Remote: %s",
|
||||
info.id.to_string(),
|
||||
printStorePath(oldR->outPath),
|
||||
printStorePath(info.outPath)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
state->stmts->RegisterRealisedOutput.use()
|
||||
(info.id.strHash())
|
||||
(info.id.outputName)
|
||||
(printStorePath(info.outPath))
|
||||
(concatStringsSep(" ", info.signatures))
|
||||
.exec();
|
||||
}
|
||||
uint64_t myId = state->db.getLastInsertedRowId();
|
||||
for (auto & [outputId, depPath] : info.dependentRealisations) {
|
||||
auto localRealisation = queryRealisationCore_(*state, outputId);
|
||||
if (!localRealisation)
|
||||
throw Error("unable to register the derivation '%s' as it "
|
||||
"depends on the non existent '%s'",
|
||||
info.id.to_string(), outputId.to_string());
|
||||
if (localRealisation->second.outPath != depPath)
|
||||
throw Error("unable to register the derivation '%s' as it "
|
||||
"depends on a realisation of '%s' that doesn’t"
|
||||
"match what we have locally",
|
||||
info.id.to_string(), outputId.to_string());
|
||||
state->stmts->AddRealisationReference.use()
|
||||
(myId)
|
||||
(outputId.strHash())
|
||||
(outputId.outputName)
|
||||
.exec();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1102,15 +1205,20 @@ const PublicKeys & LocalStore::getPublicKeys()
|
|||
return *state->publicKeys;
|
||||
}
|
||||
|
||||
bool LocalStore::pathInfoIsTrusted(const ValidPathInfo & info)
|
||||
bool LocalStore::pathInfoIsUntrusted(const ValidPathInfo & info)
|
||||
{
|
||||
return requireSigs && !info.checkSignatures(*this, getPublicKeys());
|
||||
}
|
||||
|
||||
bool LocalStore::realisationIsUntrusted(const Realisation & realisation)
|
||||
{
|
||||
return requireSigs && !realisation.checkSignatures(getPublicKeys());
|
||||
}
|
||||
|
||||
void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs)
|
||||
{
|
||||
if (checkSigs && pathInfoIsTrusted(info))
|
||||
if (checkSigs && pathInfoIsUntrusted(info))
|
||||
throw Error("cannot add path '%s' because it lacks a valid signature", printStorePath(info.path));
|
||||
|
||||
addTempRoot(info.path);
|
||||
|
@ -1138,17 +1246,13 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
|||
|
||||
/* While restoring the path from the NAR, compute the hash
|
||||
of the NAR. */
|
||||
std::unique_ptr<AbstractHashSink> hashSink;
|
||||
if (!info.ca.has_value() || !info.references.count(info.path))
|
||||
hashSink = std::make_unique<HashSink>(htSHA256);
|
||||
else
|
||||
hashSink = std::make_unique<HashModuloSink>(htSHA256, std::string(info.path.hashPart()));
|
||||
HashSink hashSink(htSHA256);
|
||||
|
||||
TeeSource wrapperSource { source, *hashSink };
|
||||
TeeSource wrapperSource { source, hashSink };
|
||||
|
||||
restorePath(realPath, wrapperSource);
|
||||
|
||||
auto hashResult = hashSink->finish();
|
||||
auto hashResult = hashSink.finish();
|
||||
|
||||
if (hashResult.first != info.narHash)
|
||||
throw Error("hash mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||
|
@ -1158,6 +1262,31 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
|||
throw Error("size mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||
printStorePath(info.path), info.narSize, hashResult.second);
|
||||
|
||||
if (info.ca) {
|
||||
if (auto foHash = std::get_if<FixedOutputHash>(&*info.ca)) {
|
||||
auto actualFoHash = hashCAPath(
|
||||
foHash->method,
|
||||
foHash->hash.type,
|
||||
info.path
|
||||
);
|
||||
if (foHash->hash != actualFoHash.hash) {
|
||||
throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||
printStorePath(info.path),
|
||||
foHash->hash.to_string(Base32, true),
|
||||
actualFoHash.hash.to_string(Base32, true));
|
||||
}
|
||||
}
|
||||
if (auto textHash = std::get_if<TextHash>(&*info.ca)) {
|
||||
auto actualTextHash = hashString(htSHA256, readFile(realPath));
|
||||
if (textHash->hash != actualTextHash) {
|
||||
throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||
printStorePath(info.path),
|
||||
textHash->hash.to_string(Base32, true),
|
||||
actualTextHash.to_string(Base32, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
autoGC();
|
||||
|
||||
canonicalisePathMetaData(realPath, -1);
|
||||
|
@ -1426,14 +1555,10 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
|||
/* Check the content hash (optionally - slow). */
|
||||
printMsg(lvlTalkative, "checking contents of '%s'", printStorePath(i));
|
||||
|
||||
std::unique_ptr<AbstractHashSink> hashSink;
|
||||
if (!info->ca || !info->references.count(info->path))
|
||||
hashSink = std::make_unique<HashSink>(info->narHash.type);
|
||||
else
|
||||
hashSink = std::make_unique<HashModuloSink>(info->narHash.type, std::string(info->path.hashPart()));
|
||||
auto hashSink = HashSink(info->narHash.type);
|
||||
|
||||
dumpPath(Store::toRealPath(i), *hashSink);
|
||||
auto current = hashSink->finish();
|
||||
dumpPath(Store::toRealPath(i), hashSink);
|
||||
auto current = hashSink.finish();
|
||||
|
||||
if (info->narHash != nullHash && info->narHash != current.first) {
|
||||
printError("path '%s' was modified! expected hash '%s', got '%s'",
|
||||
|
@ -1612,6 +1737,18 @@ void LocalStore::addSignatures(const StorePath & storePath, const StringSet & si
|
|||
}
|
||||
|
||||
|
||||
void LocalStore::signRealisation(Realisation & realisation)
|
||||
{
|
||||
// FIXME: keep secret keys in memory.
|
||||
|
||||
auto secretKeyFiles = settings.secretKeyFiles;
|
||||
|
||||
for (auto & secretKeyFile : secretKeyFiles.get()) {
|
||||
SecretKey secretKey(readFile(secretKeyFile));
|
||||
realisation.sign(secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalStore::signPathInfo(ValidPathInfo & info)
|
||||
{
|
||||
// FIXME: keep secret keys in memory.
|
||||
|
@ -1639,18 +1776,97 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<const Realisation> LocalStore::queryRealisation(
|
||||
const DrvOutput& id) {
|
||||
typedef std::optional<const Realisation> Ret;
|
||||
return retrySQLite<Ret>([&]() -> Ret {
|
||||
auto state(_state.lock());
|
||||
auto use(state->stmts->QueryRealisedOutput.use()(id.strHash())(
|
||||
id.outputName));
|
||||
if (!use.next())
|
||||
std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
|
||||
LocalStore::State & state,
|
||||
const DrvOutput & id)
|
||||
{
|
||||
auto useQueryRealisedOutput(
|
||||
state.stmts->QueryRealisedOutput.use()
|
||||
(id.strHash())
|
||||
(id.outputName));
|
||||
if (!useQueryRealisedOutput.next())
|
||||
return std::nullopt;
|
||||
auto outputPath = parseStorePath(use.getStr(0));
|
||||
return Ret{
|
||||
Realisation{.id = id, .outPath = outputPath}};
|
||||
auto realisationDbId = useQueryRealisedOutput.getInt(0);
|
||||
auto outputPath = parseStorePath(useQueryRealisedOutput.getStr(1));
|
||||
auto signatures =
|
||||
tokenizeString<StringSet>(useQueryRealisedOutput.getStr(2));
|
||||
|
||||
return {{
|
||||
realisationDbId,
|
||||
Realisation{
|
||||
.id = id,
|
||||
.outPath = outputPath,
|
||||
.signatures = signatures,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
std::optional<const Realisation> LocalStore::queryRealisation_(
|
||||
LocalStore::State & state,
|
||||
const DrvOutput & id)
|
||||
{
|
||||
auto maybeCore = queryRealisationCore_(state, id);
|
||||
if (!maybeCore)
|
||||
return std::nullopt;
|
||||
auto [realisationDbId, res] = *maybeCore;
|
||||
|
||||
std::map<DrvOutput, StorePath> dependentRealisations;
|
||||
auto useRealisationRefs(
|
||||
state.stmts->QueryRealisationReferences.use()
|
||||
(realisationDbId));
|
||||
while (useRealisationRefs.next()) {
|
||||
auto depId = DrvOutput {
|
||||
Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)),
|
||||
useRealisationRefs.getStr(1),
|
||||
};
|
||||
auto dependentRealisation = queryRealisationCore_(state, depId);
|
||||
assert(dependentRealisation); // Enforced by the db schema
|
||||
auto outputPath = dependentRealisation->second.outPath;
|
||||
dependentRealisations.insert({depId, outputPath});
|
||||
}
|
||||
|
||||
res.dependentRealisations = dependentRealisations;
|
||||
|
||||
return { res };
|
||||
}
|
||||
|
||||
std::optional<const Realisation>
|
||||
LocalStore::queryRealisation(const DrvOutput & id)
|
||||
{
|
||||
return retrySQLite<std::optional<const Realisation>>([&]() {
|
||||
auto state(_state.lock());
|
||||
return queryRealisation_(*state, id);
|
||||
});
|
||||
}
|
||||
|
||||
FixedOutputHash LocalStore::hashCAPath(
|
||||
const FileIngestionMethod & method, const HashType & hashType,
|
||||
const StorePath & path)
|
||||
{
|
||||
return hashCAPath(method, hashType, Store::toRealPath(path), path.hashPart());
|
||||
}
|
||||
|
||||
FixedOutputHash LocalStore::hashCAPath(
|
||||
const FileIngestionMethod & method,
|
||||
const HashType & hashType,
|
||||
const Path & path,
|
||||
const std::string_view pathHash
|
||||
)
|
||||
{
|
||||
HashModuloSink caSink ( hashType, std::string(pathHash) );
|
||||
switch (method) {
|
||||
case FileIngestionMethod::Recursive:
|
||||
dumpPath(path, caSink);
|
||||
break;
|
||||
case FileIngestionMethod::Flat:
|
||||
readFile(path, caSink);
|
||||
break;
|
||||
}
|
||||
auto hash = caSink.finish().first;
|
||||
return FixedOutputHash{
|
||||
.method = method,
|
||||
.hash = hash,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
@ -83,9 +83,6 @@ private:
|
|||
|
||||
public:
|
||||
|
||||
PathSetting realStoreDir_;
|
||||
|
||||
const Path realStoreDir;
|
||||
const Path dbDir;
|
||||
const Path linksDir;
|
||||
const Path reservedPath;
|
||||
|
@ -136,7 +133,8 @@ public:
|
|||
void querySubstitutablePathInfos(const StorePathCAMap & paths,
|
||||
SubstitutablePathInfos & infos) override;
|
||||
|
||||
bool pathInfoIsTrusted(const ValidPathInfo &) override;
|
||||
bool pathInfoIsUntrusted(const ValidPathInfo &) override;
|
||||
bool realisationIsUntrusted(const Realisation & ) override;
|
||||
|
||||
void addToStore(const ValidPathInfo & info, Source & source,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
||||
|
@ -202,8 +200,11 @@ public:
|
|||
/* Register the store path 'output' as the output named 'outputName' of
|
||||
derivation 'deriver'. */
|
||||
void registerDrvOutput(const Realisation & info) override;
|
||||
void registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) override;
|
||||
void cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output);
|
||||
|
||||
std::optional<const Realisation> queryRealisation_(State & state, const DrvOutput & id);
|
||||
std::optional<std::pair<int64_t, Realisation>> queryRealisationCore_(State & state, const DrvOutput & id);
|
||||
std::optional<const Realisation> queryRealisation(const DrvOutput&) override;
|
||||
|
||||
private:
|
||||
|
@ -272,16 +273,30 @@ private:
|
|||
bool isValidPath_(State & state, const StorePath & path);
|
||||
void queryReferrers(State & state, const StorePath & path, StorePathSet & referrers);
|
||||
|
||||
/* Add signatures to a ValidPathInfo using the secret keys
|
||||
/* Add signatures to a ValidPathInfo or Realisation using the secret keys
|
||||
specified by the ‘secret-key-files’ option. */
|
||||
void signPathInfo(ValidPathInfo & info);
|
||||
|
||||
Path getRealStoreDir() override { return realStoreDir; }
|
||||
void signRealisation(Realisation &);
|
||||
|
||||
void createUser(const std::string & userName, uid_t userId) override;
|
||||
|
||||
// XXX: Make a generic `Store` method
|
||||
FixedOutputHash hashCAPath(
|
||||
const FileIngestionMethod & method,
|
||||
const HashType & hashType,
|
||||
const StorePath & path);
|
||||
|
||||
FixedOutputHash hashCAPath(
|
||||
const FileIngestionMethod & method,
|
||||
const HashType & hashType,
|
||||
const Path & path,
|
||||
const std::string_view pathHash
|
||||
);
|
||||
|
||||
friend struct LocalDerivationGoal;
|
||||
friend struct PathSubstitutionGoal;
|
||||
friend struct SubstitutionGoal;
|
||||
friend struct DerivationGoal;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
|
|||
libstore_LIBS = libutil
|
||||
|
||||
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
||||
ifneq ($(OS), FreeBSD)
|
||||
ifeq ($(OS), Linux)
|
||||
libstore_LDFLAGS += -ldl
|
||||
endif
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue