Merge remote-tracking branch 'upstream/master' into path-info

This commit is contained in:
John Ericson 2021-02-25 20:35:11 +00:00
commit ca0994819d
344 changed files with 12008 additions and 6500 deletions

35
.github/STALE-BOT.md vendored Normal file
View file

@ -0,0 +1,35 @@
# Stale bot information
- 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).
## Suggestions for PRs
1. GitHub sometimes doesn't notify people who commented / reviewed a PR previously, when you (force) push commits. If you have addressed the reviews you can [officially ask for a review](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/requesting-a-pull-request-review) from those who commented to you or anyone else.
2. If it is unfinished but you plan to finish it, please mark it as a draft.
3. If you don't expect to work on it any time soon, closing it with a short comment may encourage someone else to pick up your work.
4. To get things rolling again, rebase the PR against the target branch and address valid comments.
5. If you need a review to move forward, ask in [the Discourse thread for PRs that need help](https://discourse.nixos.org/t/prs-in-distress/3604).
6. If all you need is a merge, check the git history to find and [request reviews](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/requesting-a-pull-request-review) from people who usually merge related contributions.
## Suggestions for issues
1. If it is resolved (either for you personally, or in general), please consider closing it.
2. If this might still be an issue, but you are not interested in promoting its resolution, please consider closing it while encouraging others to take over and reopen an issue if they care enough.
3. If you still have interest in resolving it, try to ping somebody who you believe might have an interest in the topic. Consider discussing the problem in [our Discourse Forum](https://discourse.nixos.org/).
4. As with all open source projects, your best option is to submit a Pull Request that addresses this issue. We :heart: this attitude!
**Memorandum on closing issues**
Don't be afraid to close an issue that holds valuable information. Closed issues stay in the system for people to search, read, cross-reference, or even reopen--nothing is lost! Closing obsolete issues is an important way to help maintainers focus their time and effort.
## Useful GitHub search queries
- [Open PRs with any stale-bot interaction](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+commenter%3Aapp%2Fstale+)
- [Open PRs with any stale-bot interaction and `stale`](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+commenter%3Aapp%2Fstale+label%3A%22stale%22)
- [Open PRs with any stale-bot interaction and NOT `stale`](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+commenter%3Aapp%2Fstale+-label%3A%22stale%22+)
- [Open Issues with any stale-bot interaction](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+commenter%3Aapp%2Fstale+)
- [Open Issues with any stale-bot interaction and `stale`](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+commenter%3Aapp%2Fstale+label%3A%22stale%22+)
- [Open Issues with any stale-bot interaction and NOT `stale`](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+commenter%3Aapp%2Fstale+-label%3A%22stale%22+)

10
.github/stale.yml vendored Normal file
View file

@ -0,0 +1,10 @@
# Configuration for probot-stale - https://github.com/probot/stale
daysUntilStale: 180
daysUntilClose: 365
exemptLabels:
- "critical"
staleLabel: "stale"
markComment: |
I marked this as stale due to inactivity. → [More info](https://github.com/NixOS/nix/blob/master/.github/STALE-BOT.md)
closeComment: |
I closed this issue due to inactivity. → [More info](https://github.com/NixOS/nix/blob/master/.github/STALE-BOT.md)

View file

@ -8,10 +8,52 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, macos-latest] os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env:
CACHIX_NAME: nix-ci
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2.3.4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: cachix/install-nix-action@v11 - uses: cachix/install-nix-action@v12
- uses: cachix/cachix-action@v8
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
#- run: nix flake check #- run: nix flake check
- run: nix-build -A checks.$(if [[ `uname` = Linux ]]; then echo x86_64-linux; else echo x86_64-darwin; fi) - 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
runs-on: ubuntu-latest
env:
CACHIX_NAME: nix-ci
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
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
- id: prepare-installer
run: scripts/prepare-installer-for-github-actions
installer_test:
if: github.event_name == 'push'
needs: installer
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
with:
install_url: '${{needs.installer.outputs.installerURL}}'
install_options: '--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve'
- run: nix-instantiate -E 'builtins.currentTime' --eval

4
.gitignore vendored
View file

@ -18,13 +18,13 @@ perl/Makefile.config
/doc/manual/nix.json /doc/manual/nix.json
/doc/manual/conf-file.json /doc/manual/conf-file.json
/doc/manual/builtins.json /doc/manual/builtins.json
/doc/manual/src/command-ref/nix.md /doc/manual/src/SUMMARY.md
/doc/manual/src/command-ref/new-cli
/doc/manual/src/command-ref/conf-file.md /doc/manual/src/command-ref/conf-file.md
/doc/manual/src/expressions/builtins.md /doc/manual/src/expressions/builtins.md
# /scripts/ # /scripts/
/scripts/nix-profile.sh /scripts/nix-profile.sh
/scripts/nix-copy-closure
/scripts/nix-reduce-build /scripts/nix-reduce-build
/scripts/nix-http-export.cgi /scripts/nix-http-export.cgi
/scripts/nix-profile-daemon.sh /scripts/nix-profile-daemon.sh

View file

@ -1 +1 @@
3.0 2.4

View file

@ -7,10 +7,10 @@ makefiles = \
src/libfetchers/local.mk \ src/libfetchers/local.mk \
src/libmain/local.mk \ src/libmain/local.mk \
src/libexpr/local.mk \ src/libexpr/local.mk \
src/libcmd/local.mk \
src/nix/local.mk \ src/nix/local.mk \
src/resolve-system-dependencies/local.mk \ src/resolve-system-dependencies/local.mk \
scripts/local.mk \ scripts/local.mk \
corepkgs/local.mk \
misc/bash/local.mk \ misc/bash/local.mk \
misc/systemd/local.mk \ misc/systemd/local.mk \
misc/launchd/local.mk \ misc/launchd/local.mk \

View file

@ -9,8 +9,8 @@ CXXFLAGS = @CXXFLAGS@
EDITLINE_LIBS = @EDITLINE_LIBS@ EDITLINE_LIBS = @EDITLINE_LIBS@
ENABLE_S3 = @ENABLE_S3@ ENABLE_S3 = @ENABLE_S3@
GTEST_LIBS = @GTEST_LIBS@ GTEST_LIBS = @GTEST_LIBS@
HAVE_LIBCPUID = @HAVE_LIBCPUID@
HAVE_SECCOMP = @HAVE_SECCOMP@ HAVE_SECCOMP = @HAVE_SECCOMP@
HAVE_SODIUM = @HAVE_SODIUM@
LDFLAGS = @LDFLAGS@ LDFLAGS = @LDFLAGS@
LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@ LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@ LIBBROTLI_LIBS = @LIBBROTLI_LIBS@

View file

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

538
config/config.guess vendored
View file

@ -1,8 +1,8 @@
#! /bin/sh #! /bin/sh
# Attempt to guess a canonical system name. # Attempt to guess a canonical system name.
# Copyright 1992-2018 Free Software Foundation, Inc. # Copyright 1992-2020 Free Software Foundation, Inc.
timestamp='2018-08-02' timestamp='2020-11-19'
# This file is free software; you can redistribute it and/or modify it # 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 # under the terms of the GNU General Public License as published by
@ -27,12 +27,12 @@ timestamp='2018-08-02'
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
# #
# You can get the latest version of this script from: # You can get the latest version of this script from:
# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
# #
# Please send patches to <config-patches@gnu.org>. # Please send patches to <config-patches@gnu.org>.
me=`echo "$0" | sed -e 's,.*/,,'` me=$(echo "$0" | sed -e 's,.*/,,')
usage="\ usage="\
Usage: $0 [OPTION] Usage: $0 [OPTION]
@ -50,7 +50,7 @@ version="\
GNU config.guess ($timestamp) GNU config.guess ($timestamp)
Originally written by Per Bothner. Originally written by Per Bothner.
Copyright 1992-2018 Free Software Foundation, Inc. Copyright 1992-2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@ -96,13 +96,14 @@ fi
tmp= tmp=
# shellcheck disable=SC2172 # shellcheck disable=SC2172
trap 'test -z "$tmp" || rm -fr "$tmp"' 1 2 13 15 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
trap 'exitcode=$?; test -z "$tmp" || rm -fr "$tmp"; exit $exitcode' 0
set_cc_for_build() { set_cc_for_build() {
# prevent multiple calls if $tmp is already set
test "$tmp" && return 0
: "${TMPDIR=/tmp}" : "${TMPDIR=/tmp}"
# shellcheck disable=SC2039 # shellcheck disable=SC2039
{ tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { tmp=$( (umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null) && test -n "$tmp" && test -d "$tmp" ; } ||
{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
@ -130,16 +131,14 @@ if test -f /.attbin/uname ; then
PATH=$PATH:/.attbin ; export PATH PATH=$PATH:/.attbin ; export PATH
fi fi
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown
case "$UNAME_SYSTEM" in case "$UNAME_SYSTEM" in
Linux|GNU|GNU/*) Linux|GNU|GNU/*)
# If the system lacks a compiler, then just pick glibc. LIBC=unknown
# We could probably try harder.
LIBC=gnu
set_cc_for_build set_cc_for_build
cat <<-EOF > "$dummy.c" cat <<-EOF > "$dummy.c"
@ -148,17 +147,29 @@ Linux|GNU|GNU/*)
LIBC=uclibc LIBC=uclibc
#elif defined(__dietlibc__) #elif defined(__dietlibc__)
LIBC=dietlibc LIBC=dietlibc
#else #elif defined(__GLIBC__)
LIBC=gnu LIBC=gnu
#else
#include <stdarg.h>
/* First heuristic to detect musl libc. */
#ifdef __DEFINED_va_list
LIBC=musl
#endif
#endif #endif
EOF EOF
eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')"
# If ldd exists, use it to detect musl libc. # Second heuristic to detect musl libc.
if command -v ldd >/dev/null && \ if [ "$LIBC" = unknown ] &&
ldd --version 2>&1 | grep -q ^musl command -v ldd >/dev/null &&
then ldd --version 2>&1 | grep -q ^musl; then
LIBC=musl LIBC=musl
fi
# If the system lacks a compiler, then just pick glibc.
# We could probably try harder.
if [ "$LIBC" = unknown ]; then
LIBC=gnu
fi fi
;; ;;
esac esac
@ -178,19 +189,20 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
# Note: NetBSD doesn't particularly care about the vendor # Note: NetBSD doesn't particularly care about the vendor
# portion of the name. We always set it to "unknown". # portion of the name. We always set it to "unknown".
sysctl="sysctl -n hw.machine_arch" sysctl="sysctl -n hw.machine_arch"
UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \
"/sbin/$sysctl" 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \
"/usr/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \
echo unknown)` echo unknown))
case "$UNAME_MACHINE_ARCH" in case "$UNAME_MACHINE_ARCH" in
aarch64eb) machine=aarch64_be-unknown ;;
armeb) machine=armeb-unknown ;; armeb) machine=armeb-unknown ;;
arm*) machine=arm-unknown ;; arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;; sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;; sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;; sh5el) machine=sh5le-unknown ;;
earmv*) earmv*)
arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,')
endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p')
machine="${arch}${endian}"-unknown machine="${arch}${endian}"-unknown
;; ;;
*) machine="$UNAME_MACHINE_ARCH"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
@ -221,7 +233,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
case "$UNAME_MACHINE_ARCH" in case "$UNAME_MACHINE_ARCH" in
earm*) earm*)
expr='s/^earmv[0-9]/-eabi/;s/eb$//' expr='s/^earmv[0-9]/-eabi/;s/eb$//'
abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr")
;; ;;
esac esac
# The OS release # The OS release
@ -234,7 +246,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
release='-gnu' release='-gnu'
;; ;;
*) *)
release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2)
;; ;;
esac esac
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
@ -243,15 +255,15 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
echo "$machine-${os}${release}${abi-}" echo "$machine-${os}${release}${abi-}"
exit ;; exit ;;
*:Bitrig:*:*) *:Bitrig:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//')
echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
exit ;; exit ;;
*:OpenBSD:*:*) *:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//')
echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
exit ;; exit ;;
*:LibertyBSD:*:*) *:LibertyBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//')
echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
exit ;; exit ;;
*:MidnightBSD:*:*) *:MidnightBSD:*:*)
@ -263,6 +275,9 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
*:SolidBSD:*:*) *:SolidBSD:*:*)
echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
exit ;; exit ;;
*:OS108:*:*)
echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE"
exit ;;
macppc:MirBSD:*:*) macppc:MirBSD:*:*)
echo powerpc-unknown-mirbsd"$UNAME_RELEASE" echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
exit ;; exit ;;
@ -272,26 +287,29 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
*:Sortix:*:*) *:Sortix:*:*)
echo "$UNAME_MACHINE"-unknown-sortix echo "$UNAME_MACHINE"-unknown-sortix
exit ;; exit ;;
*:Twizzler:*:*)
echo "$UNAME_MACHINE"-unknown-twizzler
exit ;;
*:Redox:*:*) *:Redox:*:*)
echo "$UNAME_MACHINE"-unknown-redox echo "$UNAME_MACHINE"-unknown-redox
exit ;; exit ;;
mips:OSF1:*.*) mips:OSF1:*.*)
echo mips-dec-osf1 echo mips-dec-osf1
exit ;; exit ;;
alpha:OSF1:*:*) alpha:OSF1:*:*)
case $UNAME_RELEASE in case $UNAME_RELEASE in
*4.0) *4.0)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}')
;; ;;
*5.*) *5.*)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}')
;; ;;
esac esac
# According to Compaq, /usr/sbin/psrinfo has been available on # According to Compaq, /usr/sbin/psrinfo has been available on
# OSF/1 and Tru64 systems produced since 1995. I hope that # OSF/1 and Tru64 systems produced since 1995. I hope that
# covers most systems running today. This code pipes the CPU # covers most systems running today. This code pipes the CPU
# types through head -n 1, so we only detect the type of CPU 0. # types through head -n 1, so we only detect the type of CPU 0.
ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` ALPHA_CPU_TYPE=$(/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1)
case "$ALPHA_CPU_TYPE" in case "$ALPHA_CPU_TYPE" in
"EV4 (21064)") "EV4 (21064)")
UNAME_MACHINE=alpha ;; UNAME_MACHINE=alpha ;;
@ -329,7 +347,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
# A Tn.n version is a released field test version. # A Tn.n version is a released field test version.
# A Xn.n version is an unreleased experimental baselevel. # A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r. # 1.2 uses "1.2" for uname -r.
echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" echo "$UNAME_MACHINE"-dec-osf"$(echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)"
# Reset EXIT trap before exiting to avoid spurious non-zero exit code. # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
exitcode=$? exitcode=$?
trap '' 0 trap '' 0
@ -363,7 +381,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
exit ;; exit ;;
Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
if test "`(/bin/universe) 2>/dev/null`" = att ; then if test "$( (/bin/universe) 2>/dev/null)" = att ; then
echo pyramid-pyramid-sysv3 echo pyramid-pyramid-sysv3
else else
echo pyramid-pyramid-bsd echo pyramid-pyramid-bsd
@ -376,54 +394,59 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
echo sparc-icl-nx6 echo sparc-icl-nx6
exit ;; exit ;;
DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
case `/usr/bin/uname -p` in case $(/usr/bin/uname -p) in
sparc) echo sparc-icl-nx7; exit ;; sparc) echo sparc-icl-nx7; exit ;;
esac ;; esac ;;
s390x:SunOS:*:*) s390x:SunOS:*:*)
echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')"
exit ;; exit ;;
sun4H:SunOS:5.*:*) sun4H:SunOS:5.*:*)
echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;; exit ;;
sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')"
exit ;; exit ;;
i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
echo i386-pc-auroraux"$UNAME_RELEASE" echo i386-pc-auroraux"$UNAME_RELEASE"
exit ;; exit ;;
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
UNAME_REL="`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" set_cc_for_build
case `isainfo -b` in SUN_ARCH=i386
32) # If there is a compiler, see if it is configured for 64-bit objects.
echo i386-pc-solaris2"$UNAME_REL" # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
;; # This test works for both compilers.
64) if test "$CC_FOR_BUILD" != no_compiler_found; then
echo x86_64-pc-solaris2"$UNAME_REL" if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
;; (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
esac grep IS_64BIT_ARCH >/dev/null
then
SUN_ARCH=x86_64
fi
fi
echo "$SUN_ARCH"-pc-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;; exit ;;
sun4*:SunOS:6*:*) sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize # According to config.sub, this is the proper way to canonicalize
# SunOS6. Hard to guess exactly what SunOS6 will be like, but # SunOS6. Hard to guess exactly what SunOS6 will be like, but
# it's likely to be more like Solaris than SunOS4. # it's likely to be more like Solaris than SunOS4.
echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;; exit ;;
sun4*:SunOS:*:*) sun4*:SunOS:*:*)
case "`/usr/bin/arch -k`" in case "$(/usr/bin/arch -k)" in
Series*|S4*) Series*|S4*)
UNAME_RELEASE=`uname -v` UNAME_RELEASE=$(uname -v)
;; ;;
esac esac
# Japanese Language versions have a version number like `4.1.3-JL'. # Japanese Language versions have a version number like `4.1.3-JL'.
echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')"
exit ;; exit ;;
sun3*:SunOS:*:*) sun3*:SunOS:*:*)
echo m68k-sun-sunos"$UNAME_RELEASE" echo m68k-sun-sunos"$UNAME_RELEASE"
exit ;; exit ;;
sun*:*:4.2BSD:*) sun*:*:4.2BSD:*)
UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null)
test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
case "`/bin/arch`" in case "$(/bin/arch)" in
sun3) sun3)
echo m68k-sun-sunos"$UNAME_RELEASE" echo m68k-sun-sunos"$UNAME_RELEASE"
;; ;;
@ -503,8 +526,8 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
} }
EOF EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" && $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') &&
SYSTEM_NAME=`"$dummy" "$dummyarg"` && SYSTEM_NAME=$("$dummy" "$dummyarg") &&
{ echo "$SYSTEM_NAME"; exit; } { echo "$SYSTEM_NAME"; exit; }
echo mips-mips-riscos"$UNAME_RELEASE" echo mips-mips-riscos"$UNAME_RELEASE"
exit ;; exit ;;
@ -531,11 +554,11 @@ EOF
exit ;; exit ;;
AViiON:dgux:*:*) AViiON:dgux:*:*)
# DG/UX returns AViiON for all architectures # DG/UX returns AViiON for all architectures
UNAME_PROCESSOR=`/usr/bin/uname -p` UNAME_PROCESSOR=$(/usr/bin/uname -p)
if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
then then
if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
[ "$TARGET_BINARY_INTERFACE"x = x ] test "$TARGET_BINARY_INTERFACE"x = x
then then
echo m88k-dg-dgux"$UNAME_RELEASE" echo m88k-dg-dgux"$UNAME_RELEASE"
else else
@ -559,17 +582,17 @@ EOF
echo m68k-tektronix-bsd echo m68k-tektronix-bsd
exit ;; exit ;;
*:IRIX*:*:*) *:IRIX*:*:*)
echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')"
exit ;; exit ;;
????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX '
i*86:AIX:*:*) i*86:AIX:*:*)
echo i386-ibm-aix echo i386-ibm-aix
exit ;; exit ;;
ia64:AIX:*:*) ia64:AIX:*:*)
if [ -x /usr/bin/oslevel ] ; then if test -x /usr/bin/oslevel ; then
IBM_REV=`/usr/bin/oslevel` IBM_REV=$(/usr/bin/oslevel)
else else
IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
fi fi
@ -589,7 +612,7 @@ EOF
exit(0); exit(0);
} }
EOF EOF
if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy")
then then
echo "$SYSTEM_NAME" echo "$SYSTEM_NAME"
else else
@ -602,15 +625,15 @@ EOF
fi fi
exit ;; exit ;;
*:AIX:*:[4567]) *:AIX:*:[4567])
IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` IBM_CPU_ID=$(/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }')
if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000 IBM_ARCH=rs6000
else else
IBM_ARCH=powerpc IBM_ARCH=powerpc
fi fi
if [ -x /usr/bin/lslpp ] ; then if test -x /usr/bin/lslpp ; then
IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc |
awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` awk -F: '{ print $3 }' | sed s/[0-9]*$/0/)
else else
IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
fi fi
@ -638,14 +661,14 @@ EOF
echo m68k-hp-bsd4.4 echo m68k-hp-bsd4.4
exit ;; exit ;;
9000/[34678]??:HP-UX:*:*) 9000/[34678]??:HP-UX:*:*)
HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//')
case "$UNAME_MACHINE" in case "$UNAME_MACHINE" in
9000/31?) HP_ARCH=m68000 ;; 9000/31?) HP_ARCH=m68000 ;;
9000/[34]??) HP_ARCH=m68k ;; 9000/[34]??) HP_ARCH=m68k ;;
9000/[678][0-9][0-9]) 9000/[678][0-9][0-9])
if [ -x /usr/bin/getconf ]; then if test -x /usr/bin/getconf; then
sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null)
sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null)
case "$sc_cpu_version" in case "$sc_cpu_version" in
523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
@ -657,7 +680,7 @@ EOF
esac ;; esac ;;
esac esac
fi fi
if [ "$HP_ARCH" = "" ]; then if test "$HP_ARCH" = ""; then
set_cc_for_build set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c" sed 's/^ //' << EOF > "$dummy.c"
@ -692,11 +715,11 @@ EOF
exit (0); exit (0);
} }
EOF EOF
(CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy")
test -z "$HP_ARCH" && HP_ARCH=hppa test -z "$HP_ARCH" && HP_ARCH=hppa
fi ;; fi ;;
esac esac
if [ "$HP_ARCH" = hppa2.0w ] if test "$HP_ARCH" = hppa2.0w
then then
set_cc_for_build set_cc_for_build
@ -720,7 +743,7 @@ EOF
echo "$HP_ARCH"-hp-hpux"$HPUX_REV" echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
exit ;; exit ;;
ia64:HP-UX:*:*) ia64:HP-UX:*:*)
HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//')
echo ia64-hp-hpux"$HPUX_REV" echo ia64-hp-hpux"$HPUX_REV"
exit ;; exit ;;
3050*:HI-UX:*:*) 3050*:HI-UX:*:*)
@ -750,7 +773,7 @@ EOF
exit (0); exit (0);
} }
EOF EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") &&
{ echo "$SYSTEM_NAME"; exit; } { echo "$SYSTEM_NAME"; exit; }
echo unknown-hitachi-hiuxwe2 echo unknown-hitachi-hiuxwe2
exit ;; exit ;;
@ -770,7 +793,7 @@ EOF
echo hppa1.0-hp-osf echo hppa1.0-hp-osf
exit ;; exit ;;
i*86:OSF1:*:*) i*86:OSF1:*:*)
if [ -x /usr/sbin/sysversion ] ; then if test -x /usr/sbin/sysversion ; then
echo "$UNAME_MACHINE"-unknown-osf1mk echo "$UNAME_MACHINE"-unknown-osf1mk
else else
echo "$UNAME_MACHINE"-unknown-osf1 echo "$UNAME_MACHINE"-unknown-osf1
@ -819,14 +842,14 @@ EOF
echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;; exit ;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)
FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///')
FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/')
echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;; exit ;;
5000:UNIX_System_V:4.*:*) 5000:UNIX_System_V:4.*:*)
FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///')
FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/')
echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;; exit ;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
@ -838,26 +861,26 @@ EOF
*:BSD/OS:*:*) *:BSD/OS:*:*)
echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
exit ;; exit ;;
arm*:FreeBSD:*:*) arm:FreeBSD:*:*)
UNAME_PROCESSOR=`uname -p` UNAME_PROCESSOR=$(uname -p)
set_cc_for_build set_cc_for_build
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_PCS_VFP | grep -q __ARM_PCS_VFP
then then
echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi
else else
echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf
fi fi
exit ;; exit ;;
*:FreeBSD:*:*) *:FreeBSD:*:*)
UNAME_PROCESSOR=`/usr/bin/uname -p` UNAME_PROCESSOR=$(/usr/bin/uname -p)
case "$UNAME_PROCESSOR" in case "$UNAME_PROCESSOR" in
amd64) amd64)
UNAME_PROCESSOR=x86_64 ;; UNAME_PROCESSOR=x86_64 ;;
i386) i386)
UNAME_PROCESSOR=i586 ;; UNAME_PROCESSOR=i586 ;;
esac esac
echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')"
exit ;; exit ;;
i*:CYGWIN*:*) i*:CYGWIN*:*)
echo "$UNAME_MACHINE"-pc-cygwin echo "$UNAME_MACHINE"-pc-cygwin
@ -890,18 +913,18 @@ EOF
echo "$UNAME_MACHINE"-pc-uwin echo "$UNAME_MACHINE"-pc-uwin
exit ;; exit ;;
amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
echo x86_64-unknown-cygwin echo x86_64-pc-cygwin
exit ;; exit ;;
prep*:SunOS:5.*:*) prep*:SunOS:5.*:*)
echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;; exit ;;
*:GNU:*:*) *:GNU:*:*)
# the GNU system # the GNU system
echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')"
exit ;; exit ;;
*:GNU/*:*:*) *:GNU/*:*:*)
# other systems with GNU libc and userland # other systems with GNU libc and userland
echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC"
exit ;; exit ;;
*:Minix:*:*) *:Minix:*:*)
echo "$UNAME_MACHINE"-unknown-minix echo "$UNAME_MACHINE"-unknown-minix
@ -914,7 +937,7 @@ EOF
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;; exit ;;
alpha:Linux:*:*) alpha:Linux:*:*)
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in
EV5) UNAME_MACHINE=alphaev5 ;; EV5) UNAME_MACHINE=alphaev5 ;;
EV56) UNAME_MACHINE=alphaev56 ;; EV56) UNAME_MACHINE=alphaev56 ;;
PCA56) UNAME_MACHINE=alphapca56 ;; PCA56) UNAME_MACHINE=alphapca56 ;;
@ -981,22 +1004,50 @@ EOF
exit ;; exit ;;
mips:Linux:*:* | mips64:Linux:*:*) mips:Linux:*:* | mips64:Linux:*:*)
set_cc_for_build set_cc_for_build
IS_GLIBC=0
test x"${LIBC}" = xgnu && IS_GLIBC=1
sed 's/^ //' << EOF > "$dummy.c" sed 's/^ //' << EOF > "$dummy.c"
#undef CPU #undef CPU
#undef ${UNAME_MACHINE} #undef mips
#undef ${UNAME_MACHINE}el #undef mipsel
#undef mips64
#undef mips64el
#if ${IS_GLIBC} && defined(_ABI64)
LIBCABI=gnuabi64
#else
#if ${IS_GLIBC} && defined(_ABIN32)
LIBCABI=gnuabin32
#else
LIBCABI=${LIBC}
#endif
#endif
#if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
CPU=mipsisa64r6
#else
#if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
CPU=mipsisa32r6
#else
#if defined(__mips64)
CPU=mips64
#else
CPU=mips
#endif
#endif
#endif
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
CPU=${UNAME_MACHINE}el MIPS_ENDIAN=el
#else #else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
CPU=${UNAME_MACHINE} MIPS_ENDIAN=
#else #else
CPU= MIPS_ENDIAN=
#endif #endif
#endif #endif
EOF EOF
eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')"
test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
;; ;;
mips64el:Linux:*:*) mips64el:Linux:*:*)
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
@ -1015,7 +1066,7 @@ EOF
exit ;; exit ;;
parisc:Linux:*:* | hppa:Linux:*:*) parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level # Look for CPU level
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in
PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
*) echo hppa-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;;
@ -1055,7 +1106,17 @@ EOF
echo "$UNAME_MACHINE"-dec-linux-"$LIBC" echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
exit ;; exit ;;
x86_64:Linux:*:*) x86_64:Linux:*:*)
echo "$UNAME_MACHINE"-pc-linux-"$LIBC" set_cc_for_build
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_X32 >/dev/null
then
LIBCABI="$LIBC"x32
fi
fi
echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI"
exit ;; exit ;;
xtensa*:Linux:*:*) xtensa*:Linux:*:*)
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
@ -1095,7 +1156,7 @@ EOF
echo "$UNAME_MACHINE"-pc-msdosdjgpp echo "$UNAME_MACHINE"-pc-msdosdjgpp
exit ;; exit ;;
i*86:*:4.*:*) i*86:*:4.*:*)
UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//')
if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
else else
@ -1104,19 +1165,19 @@ EOF
exit ;; exit ;;
i*86:*:5:[678]*) i*86:*:5:[678]*)
# UnixWare 7.x, OpenUNIX and OpenServer 6. # UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in case $(/bin/uname -X | grep "^Machine") in
*486*) UNAME_MACHINE=i486 ;; *486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;; *Pentium) UNAME_MACHINE=i586 ;;
*Pent*|*Celeron) UNAME_MACHINE=i686 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
esac esac
echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}"
exit ;; exit ;;
i*86:*:3.2:*) i*86:*:3.2:*)
if test -f /usr/options/cb.name; then if test -f /usr/options/cb.name; then
UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` UNAME_REL=$(sed -n 's/.*Version //p' </usr/options/cb.name)
echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL" echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
elif /bin/uname -X 2>/dev/null >/dev/null ; then elif /bin/uname -X 2>/dev/null >/dev/null ; then
UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //'))
(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
&& UNAME_MACHINE=i586 && UNAME_MACHINE=i586
@ -1166,7 +1227,7 @@ EOF
3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
OS_REL='' OS_REL=''
test -r /etc/.relid \ test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid)
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3"$OS_REL"; exit; } && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
@ -1177,7 +1238,7 @@ EOF
NCR*:*:4.2:* | MPRAS*:*:4.2:*) NCR*:*:4.2:* | MPRAS*:*:4.2:*)
OS_REL='.3' OS_REL='.3'
test -r /etc/.relid \ test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid)
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3"$OS_REL"; exit; } && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
@ -1210,7 +1271,7 @@ EOF
exit ;; exit ;;
*:SINIX-*:*:*) *:SINIX-*:*:*)
if uname -p 2>/dev/null >/dev/null ; then if uname -p 2>/dev/null >/dev/null ; then
UNAME_MACHINE=`(uname -p) 2>/dev/null` UNAME_MACHINE=$( (uname -p) 2>/dev/null)
echo "$UNAME_MACHINE"-sni-sysv4 echo "$UNAME_MACHINE"-sni-sysv4
else else
echo ns32k-sni-sysv echo ns32k-sni-sysv
@ -1244,7 +1305,7 @@ EOF
echo mips-sony-newsos6 echo mips-sony-newsos6
exit ;; exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then if test -d /usr/nec; then
echo mips-nec-sysv"$UNAME_RELEASE" echo mips-nec-sysv"$UNAME_RELEASE"
else else
echo mips-unknown-sysv"$UNAME_RELEASE" echo mips-unknown-sysv"$UNAME_RELEASE"
@ -1292,44 +1353,48 @@ EOF
*:Rhapsody:*:*) *:Rhapsody:*:*)
echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
exit ;; exit ;;
arm64:Darwin:*:*)
echo aarch64-apple-darwin"$UNAME_RELEASE"
exit ;;
*:Darwin:*:*) *:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown UNAME_PROCESSOR=$(uname -p)
set_cc_for_build case $UNAME_PROCESSOR in
if test "$UNAME_PROCESSOR" = unknown ; then unknown) UNAME_PROCESSOR=powerpc ;;
UNAME_PROCESSOR=powerpc esac
if command -v xcode-select > /dev/null 2> /dev/null && \
! xcode-select --print-path > /dev/null 2> /dev/null ; then
# Avoid executing cc if there is no toolchain installed as
# cc will be a stub that puts up a graphical alert
# prompting the user to install developer tools.
CC_FOR_BUILD=no_compiler_found
else
set_cc_for_build
fi fi
if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then if test "$CC_FOR_BUILD" != no_compiler_found; then
if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null
grep IS_64BIT_ARCH >/dev/null then
then case $UNAME_PROCESSOR in
case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;;
i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;;
powerpc) UNAME_PROCESSOR=powerpc64 ;; esac
esac fi
fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
# On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null
grep IS_PPC >/dev/null then
then UNAME_PROCESSOR=powerpc
UNAME_PROCESSOR=powerpc
fi
fi fi
elif test "$UNAME_PROCESSOR" = i386 ; then elif test "$UNAME_PROCESSOR" = i386 ; then
# Avoid executing cc on OS X 10.9, as it ships with a stub # uname -m returns i386 or x86_64
# that puts up a graphical alert prompting to install UNAME_PROCESSOR=$UNAME_MACHINE
# developer tools. Any system running Mac OS X 10.7 or
# later (Darwin 11 and later) is required to have a 64-bit
# processor. This is not true of the ARM version of Darwin
# that Apple uses in portable devices.
UNAME_PROCESSOR=x86_64
fi fi
echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
exit ;; exit ;;
*:procnto*:*:* | *:QNX:[0123456789]*:*) *:procnto*:*:* | *:QNX:[0123456789]*:*)
UNAME_PROCESSOR=`uname -p` UNAME_PROCESSOR=$(uname -p)
if test "$UNAME_PROCESSOR" = x86; then if test "$UNAME_PROCESSOR" = x86; then
UNAME_PROCESSOR=i386 UNAME_PROCESSOR=i386
UNAME_MACHINE=pc UNAME_MACHINE=pc
@ -1397,10 +1462,10 @@ EOF
echo mips-sei-seiux"$UNAME_RELEASE" echo mips-sei-seiux"$UNAME_RELEASE"
exit ;; exit ;;
*:DragonFly:*:*) *:DragonFly:*:*)
echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')"
exit ;; exit ;;
*:*VMS:*:*) *:*VMS:*:*)
UNAME_MACHINE=`(uname -p) 2>/dev/null` UNAME_MACHINE=$( (uname -p) 2>/dev/null)
case "$UNAME_MACHINE" in case "$UNAME_MACHINE" in
A*) echo alpha-dec-vms ; exit ;; A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;;
@ -1410,7 +1475,7 @@ EOF
echo i386-pc-xenix echo i386-pc-xenix
exit ;; exit ;;
i*86:skyos:*:*) i*86:skyos:*:*)
echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')"
exit ;; exit ;;
i*86:rdos:*:*) i*86:rdos:*:*)
echo "$UNAME_MACHINE"-pc-rdos echo "$UNAME_MACHINE"-pc-rdos
@ -1424,8 +1489,148 @@ EOF
amd64:Isilon\ OneFS:*:*) amd64:Isilon\ OneFS:*:*)
echo x86_64-unknown-onefs echo x86_64-unknown-onefs
exit ;; exit ;;
*:Unleashed:*:*)
echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE"
exit ;;
esac esac
# No uname command or uname output not recognized.
set_cc_for_build
cat > "$dummy.c" <<EOF
#ifdef _SEQUENT_
#include <sys/types.h>
#include <sys/utsname.h>
#endif
#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
#include <signal.h>
#if defined(_SIZE_T_) || defined(SIGLOST)
#include <sys/utsname.h>
#endif
#endif
#endif
main ()
{
#if defined (sony)
#if defined (MIPSEB)
/* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
I don't know.... */
printf ("mips-sony-bsd\n"); exit (0);
#else
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
"4"
#else
""
#endif
); exit (0);
#endif
#endif
#if defined (NeXT)
#if !defined (__ARCHITECTURE__)
#define __ARCHITECTURE__ "m68k"
#endif
int version;
version=$( (hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null);
if (version < 4)
printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
else
printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
exit (0);
#endif
#if defined (MULTIMAX) || defined (n16)
#if defined (UMAXV)
printf ("ns32k-encore-sysv\n"); exit (0);
#else
#if defined (CMU)
printf ("ns32k-encore-mach\n"); exit (0);
#else
printf ("ns32k-encore-bsd\n"); exit (0);
#endif
#endif
#endif
#if defined (__386BSD__)
printf ("i386-pc-bsd\n"); exit (0);
#endif
#if defined (sequent)
#if defined (i386)
printf ("i386-sequent-dynix\n"); exit (0);
#endif
#if defined (ns32000)
printf ("ns32k-sequent-dynix\n"); exit (0);
#endif
#endif
#if defined (_SEQUENT_)
struct utsname un;
uname(&un);
if (strncmp(un.version, "V2", 2) == 0) {
printf ("i386-sequent-ptx2\n"); exit (0);
}
if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
printf ("i386-sequent-ptx1\n"); exit (0);
}
printf ("i386-sequent-ptx\n"); exit (0);
#endif
#if defined (vax)
#if !defined (ultrix)
#include <sys/param.h>
#if defined (BSD)
#if BSD == 43
printf ("vax-dec-bsd4.3\n"); exit (0);
#else
#if BSD == 199006
printf ("vax-dec-bsd4.3reno\n"); exit (0);
#else
printf ("vax-dec-bsd\n"); exit (0);
#endif
#endif
#else
printf ("vax-dec-bsd\n"); exit (0);
#endif
#else
#if defined(_SIZE_T_) || defined(SIGLOST)
struct utsname un;
uname (&un);
printf ("vax-dec-ultrix%s\n", un.release); exit (0);
#else
printf ("vax-dec-ultrix\n"); exit (0);
#endif
#endif
#endif
#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
#if defined(_SIZE_T_) || defined(SIGLOST)
struct utsname *un;
uname (&un);
printf ("mips-dec-ultrix%s\n", un.release); exit (0);
#else
printf ("mips-dec-ultrix\n"); exit (0);
#endif
#endif
#endif
#if defined (alliant) && defined (i860)
printf ("i860-alliant-bsd\n"); exit (0);
#endif
exit (1);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=$($dummy) &&
{ echo "$SYSTEM_NAME"; exit; }
# Apollos put the system type in the environment.
test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
echo "$0: unable to guess system type" >&2 echo "$0: unable to guess system type" >&2
case "$UNAME_MACHINE:$UNAME_SYSTEM" in case "$UNAME_MACHINE:$UNAME_SYSTEM" in
@ -1445,9 +1650,15 @@ This script (version $timestamp), has failed to recognize the
operating system you are using. If your script is old, overwrite *all* operating system you are using. If your script is old, overwrite *all*
copies of config.guess and config.sub with the latest versions from: copies of config.guess and config.sub with the latest versions from:
https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
and and
https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
EOF
year=$(echo $timestamp | sed 's,-.*,,')
# shellcheck disable=SC2003
if test "$(expr "$(date +%Y)" - "$year")" -lt 3 ; then
cat >&2 <<EOF
If $0 has already been updated, send the following data and any If $0 has already been updated, send the following data and any
information you think might be pertinent to config-patches@gnu.org to information you think might be pertinent to config-patches@gnu.org to
@ -1455,26 +1666,27 @@ provide the necessary information to handle your system.
config.guess timestamp = $timestamp config.guess timestamp = $timestamp
uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -m = $( (uname -m) 2>/dev/null || echo unknown)
uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -r = $( (uname -r) 2>/dev/null || echo unknown)
uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -s = $( (uname -s) 2>/dev/null || echo unknown)
uname -v = `(uname -v) 2>/dev/null || echo unknown` uname -v = $( (uname -v) 2>/dev/null || echo unknown)
/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /usr/bin/uname -p = $( (/usr/bin/uname -p) 2>/dev/null)
/bin/uname -X = `(/bin/uname -X) 2>/dev/null` /bin/uname -X = $( (/bin/uname -X) 2>/dev/null)
hostinfo = `(hostinfo) 2>/dev/null` hostinfo = $( (hostinfo) 2>/dev/null)
/bin/universe = `(/bin/universe) 2>/dev/null` /bin/universe = $( (/bin/universe) 2>/dev/null)
/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null)
/bin/arch = `(/bin/arch) 2>/dev/null` /bin/arch = $( (/bin/arch) 2>/dev/null)
/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null)
/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` /usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null)
UNAME_MACHINE = "$UNAME_MACHINE" UNAME_MACHINE = "$UNAME_MACHINE"
UNAME_RELEASE = "$UNAME_RELEASE" UNAME_RELEASE = "$UNAME_RELEASE"
UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_SYSTEM = "$UNAME_SYSTEM"
UNAME_VERSION = "$UNAME_VERSION" UNAME_VERSION = "$UNAME_VERSION"
EOF EOF
fi
exit 1 exit 1

1756
config/config.sub vendored

File diff suppressed because it is too large Load diff

View file

@ -174,11 +174,15 @@ PKG_CHECK_MODULES([OPENSSL], [libcrypto], [CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS"]
# Look for libbz2, a required dependency. # Look for libbz2, a required dependency.
AC_CHECK_LIB([bz2], [BZ2_bzWriteOpen], [true], AC_CHECK_LIB([bz2], [BZ2_bzWriteOpen], [true],
[AC_MSG_ERROR([Nix requires libbz2, which is part of bzip2. See https://web.archive.org/web/20180624184756/http://www.bzip.org/.])]) [AC_MSG_ERROR([Nix requires libbz2, which is part of bzip2. See https://sourceware.org/bzip2/.])])
AC_CHECK_HEADERS([bzlib.h], [true], AC_CHECK_HEADERS([bzlib.h], [true],
[AC_MSG_ERROR([Nix requires libbz2, which is part of bzip2. See https://web.archive.org/web/20180624184756/http://www.bzip.org/.])]) [AC_MSG_ERROR([Nix requires libbz2, which is part of bzip2. See https://sourceware.org/bzip2/.])])
# Checks for libarchive # Checks for libarchive
PKG_CHECK_MODULES([LIBARCHIVE], [libarchive >= 3.1.2], [CXXFLAGS="$LIBARCHIVE_CFLAGS $CXXFLAGS"]) PKG_CHECK_MODULES([LIBARCHIVE], [libarchive >= 3.1.2], [CXXFLAGS="$LIBARCHIVE_CFLAGS $CXXFLAGS"])
# Workaround until https://github.com/libarchive/libarchive/issues/1446 is fixed
if test "$shared" != yes; then
LIBARCHIVE_LIBS+=' -lz'
fi
# Look for SQLite, a required dependency. # Look for SQLite, a required dependency.
PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= 3.6.19], [CXXFLAGS="$SQLITE3_CFLAGS $CXXFLAGS"]) PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= 3.6.19], [CXXFLAGS="$SQLITE3_CFLAGS $CXXFLAGS"])
@ -199,11 +203,7 @@ PKG_CHECK_MODULES([EDITLINE], [libeditline], [CXXFLAGS="$EDITLINE_CFLAGS $CXXFLA
]) ])
# Look for libsodium, an optional dependency. # Look for libsodium, an optional dependency.
PKG_CHECK_MODULES([SODIUM], [libsodium], PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
[AC_DEFINE([HAVE_SODIUM], [1], [Whether to use libsodium for cryptography.])
CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"
have_sodium=1], [have_sodium=])
AC_SUBST(HAVE_SODIUM, [$have_sodium])
# Look for liblzma, a required dependency. # Look for liblzma, a required dependency.
PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"]) PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"])
@ -218,6 +218,14 @@ LDFLAGS="-lz $LDFLAGS"
# Look for libbrotli{enc,dec}. # Look for libbrotli{enc,dec}.
PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"]) PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"])
# Look for libcpuid.
if test "$machine_name" = "x86_64"; then
PKG_CHECK_MODULES([LIBCPUID], [libcpuid], [CXXFLAGS="$LIBCPUID_CFLAGS $CXXFLAGS"])
have_libcpuid=1
AC_DEFINE([HAVE_LIBCPUID], [1], [Use libcpuid])
fi
AC_SUBST(HAVE_LIBCPUID, [$have_libcpuid])
# Look for libseccomp, required for Linux sandboxing. # Look for libseccomp, required for Linux sandboxing.
if test "$sys_name" = linux; then if test "$sys_name" = linux; then
@ -251,6 +259,7 @@ if test -n "$enable_s3"; then
declare -a aws_version_tokens=($(printf '#include <aws/core/VersionConfig.h>\nAWS_SDK_VERSION_STRING' | $CPP $CPPFLAGS - | grep -v '^#.*' | sed 's/"//g' | tr '.' ' ')) declare -a aws_version_tokens=($(printf '#include <aws/core/VersionConfig.h>\nAWS_SDK_VERSION_STRING' | $CPP $CPPFLAGS - | grep -v '^#.*' | sed 's/"//g' | tr '.' ' '))
AC_DEFINE_UNQUOTED([AWS_VERSION_MAJOR], ${aws_version_tokens@<:@0@:>@}, [Major version of aws-sdk-cpp.]) AC_DEFINE_UNQUOTED([AWS_VERSION_MAJOR], ${aws_version_tokens@<:@0@:>@}, [Major version of aws-sdk-cpp.])
AC_DEFINE_UNQUOTED([AWS_VERSION_MINOR], ${aws_version_tokens@<:@1@:>@}, [Minor version of aws-sdk-cpp.]) AC_DEFINE_UNQUOTED([AWS_VERSION_MINOR], ${aws_version_tokens@<:@1@:>@}, [Minor version of aws-sdk-cpp.])
AC_DEFINE_UNQUOTED([AWS_VERSION_PATCH], ${aws_version_tokens@<:@2@:>@}, [Patch version of aws-sdk-cpp.])
fi fi

View file

@ -1,4 +0,0 @@
corepkgs_FILES = \
fetchurl.nix
$(foreach file,$(corepkgs_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/corepkgs)))

File diff suppressed because it is too large Load diff

View file

@ -1,55 +1,95 @@
command:
with builtins; with builtins;
with import ./utils.nix; with import ./utils.nix;
let let
showCommand = showCommand =
{ command, section, def }: { command, def, filename }:
"${section} Name\n\n" ''
**Warning**: This program is **experimental** and its interface is subject to change.
''
+ "# Name\n\n"
+ "`${command}` - ${def.description}\n\n" + "`${command}` - ${def.description}\n\n"
+ "${section} Synopsis\n\n" + "# Synopsis\n\n"
+ showSynopsis { inherit command; args = def.args; } + showSynopsis { inherit command; args = def.args; }
+ (if def ? doc + (if def.commands or {} != {}
then "${section} Description\n\n" + def.doc + "\n\n"
else "")
+ (let s = showFlags def.flags; in
if s != ""
then "${section} Flags\n\n${s}"
else "")
+ (if def.examples or [] != []
then then
"${section} Examples\n\n" let
+ concatStrings (map ({ description, command }: "${description}\n\n```console\n${command}\n```\n\n") def.examples) categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues def.commands)));
listCommands = cmds:
concatStrings (map (name:
"* [`${command} ${name}`](./${appendName filename name}.md) - ${cmds.${name}.description}\n")
(attrNames cmds));
in
"where *subcommand* is one of the following:\n\n"
# FIXME: group by category
+ (if length categories > 1
then
concatStrings (map
(cat:
"**${toString cat.description}:**\n\n"
+ listCommands (filterAttrs (n: v: v.category == cat) def.commands)
+ "\n"
) categories)
+ "\n"
else
listCommands def.commands
+ "\n")
else "") else "")
+ (if def.commands or [] != [] + (if def ? doc
then concatStrings ( then def.doc + "\n\n"
map (name: else "")
"# Subcommand `${command} ${name}`\n\n" + (let s = showOptions def.flags; in
+ showCommand { command = command + " " + name; section = "##"; def = def.commands.${name}; }) if s != ""
(attrNames def.commands)) then "# Options\n\n${s}"
else ""); else "")
;
showFlags = flags: appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name;
concatStrings
(map (longName: showOptions = flags:
let flag = flags.${longName}; in let
if flag.category or "" != "config" categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues flags)));
then in
" - `--${longName}`" concatStrings (map
+ (if flag ? shortName then " / `${flag.shortName}`" else "") (cat:
+ (if flag ? labels then " " + (concatStringsSep " " (map (s: "*${s}*") flag.labels)) else "") (if cat != ""
+ " \n" then "**${cat}:**\n\n"
+ " " + flag.description + "\n\n" else "")
else "") + concatStrings
(attrNames flags)); (map (longName:
let
flag = flags.${longName};
in
" - `--${longName}`"
+ (if flag ? shortName then " / `-${flag.shortName}`" else "")
+ (if flag ? labels then " " + (concatStringsSep " " (map (s: "*${s}*") flag.labels)) else "")
+ " \n"
+ " " + flag.description + "\n\n"
) (attrNames (filterAttrs (n: v: v.category == cat) flags))))
categories);
showSynopsis = showSynopsis =
{ command, args }: { command, args }:
"`${command}` [*flags*...] ${concatStringsSep " " "`${command}` [*option*...] ${concatStringsSep " "
(map (arg: "*${arg.label}*" + (if arg ? arity then "" else "...")) args)}\n\n"; (map (arg: "*${arg.label}*" + (if arg ? arity then "" else "...")) args)}\n\n";
processCommand = { command, def, filename }:
[ { name = filename + ".md"; value = showCommand { inherit command def filename; }; inherit command; } ]
++ concatMap
(name: processCommand {
filename = appendName filename name;
command = command + " " + name;
def = def.commands.${name};
})
(attrNames def.commands or {});
in in
command: let
manpages = processCommand { filename = "nix"; command = "nix"; def = command; };
showCommand { command = "nix"; section = "#"; def = command; } summary = concatStrings (map (manpage: " - [${manpage.command}](command-ref/new-cli/${manpage.name})\n") manpages);
in
(listToAttrs manpages) // { "SUMMARY.md" = summary; }

View file

@ -4,7 +4,7 @@ MANUAL_SRCS := $(call rwildcard, $(d)/src, *.md)
# Generate man pages. # Generate man pages.
man-pages := $(foreach n, \ man-pages := $(foreach n, \
nix-env.1 nix-build.1 nix-shell.1 nix-store.1 nix-instantiate.1 nix.1 \ nix-env.1 nix-build.1 nix-shell.1 nix-store.1 nix-instantiate.1 \
nix-collect-garbage.1 \ nix-collect-garbage.1 \
nix-prefetch-url.1 nix-channel.1 \ nix-prefetch-url.1 nix-channel.1 \
nix-hash.1 nix-copy-closure.1 \ nix-hash.1 nix-copy-closure.1 \
@ -13,9 +13,14 @@ man-pages := $(foreach n, \
clean-files += $(d)/*.1 $(d)/*.5 $(d)/*.8 clean-files += $(d)/*.1 $(d)/*.5 $(d)/*.8
dist-files += $(man-pages) # Provide a dummy environment for nix, so that it will not access files outside the macOS sandbox.
dummy-env = env -i \
HOME=/dummy \
NIX_CONF_DIR=/dummy \
NIX_SSL_CERT_FILE=/dummy/no-ca-bundle.crt \
NIX_STATE_DIR=/dummy
nix-eval = $(bindir)/nix eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw --expr nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw
$(d)/%.1: $(d)/src/command-ref/%.md $(d)/%.1: $(d)/src/command-ref/%.md
@printf "Title: %s\n\n" "$$(basename $@ .1)" > $^.tmp @printf "Title: %s\n\n" "$$(basename $@ .1)" > $^.tmp
@ -35,37 +40,51 @@ $(d)/nix.conf.5: $(d)/src/command-ref/conf-file.md
$(trace-gen) lowdown -sT man $^.tmp -o $@ $(trace-gen) lowdown -sT man $^.tmp -o $@
@rm $^.tmp @rm $^.tmp
$(d)/src/command-ref/nix.md: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
$(trace-gen) $(nix-eval) 'import doc/manual/generate-manpage.nix (builtins.fromJSON (builtins.readFile $<))' > $@.tmp $(trace-gen) cat doc/manual/src/SUMMARY.md.in | while IFS= read line; do if [[ $$line = @manpages@ ]]; then cat doc/manual/src/command-ref/new-cli/SUMMARY.md; else echo "$$line"; fi; done > $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
@rm -rf $@
$(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix (builtins.fromJSON (builtins.readFile $<))'
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix $(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp @cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) 'import doc/manual/generate-options.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-options.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/nix.json: $(bindir)/nix $(d)/nix.json: $(bindir)/nix
$(trace-gen) $(bindir)/nix __dump-args > $@.tmp $(trace-gen) $(dummy-env) $(bindir)/nix __dump-args > $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/conf-file.json: $(bindir)/nix $(d)/conf-file.json: $(bindir)/nix
$(trace-gen) env -i NIX_CONF_DIR=/dummy HOME=/dummy NIX_SSL_CERT_FILE=/dummy/no-ca-bundle.crt $(bindir)/nix show-config --json --experimental-features nix-command > $@.tmp $(trace-gen) $(dummy-env) $(bindir)/nix show-config --json --experimental-features nix-command > $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/src/expressions/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/expressions/builtins-prefix.md $(bindir)/nix $(d)/src/expressions/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/expressions/builtins-prefix.md $(bindir)/nix
@cat doc/manual/src/expressions/builtins-prefix.md > $@.tmp @cat doc/manual/src/expressions/builtins-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp
@mv $@.tmp $@ @mv $@.tmp $@
$(d)/builtins.json: $(bindir)/nix $(d)/builtins.json: $(bindir)/nix
$(trace-gen) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-builtins > $@.tmp $(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-builtins > $@.tmp
mv $@.tmp $@ @mv $@.tmp $@
# Generate the HTML manual. # Generate the HTML manual.
install: $(docdir)/manual/index.html install: $(docdir)/manual/index.html
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/custom.css $(d)/src/command-ref/nix.md $(d)/src/command-ref/conf-file.md $(d)/src/expressions/builtins.md # Generate 'nix' manpages.
$(trace-gen) mdbook build doc/manual -d $(docdir)/manual install: $(d)/src/command-ref/new-cli
$(trace-gen) for i in doc/manual/src/command-ref/new-cli/*.md; do \
name=$$(basename $$i .md); \
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; \
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
$(trace-gen) RUST_LOG=warn mdbook build doc/manual -d $(docdir)/manual
@cp doc/manual/highlight.pack.js $(docdir)/manual/highlight.js @cp doc/manual/highlight.pack.js $(docdir)/manual/highlight.js
endif endif

View file

@ -62,11 +62,13 @@
- [nix-instantiate](command-ref/nix-instantiate.md) - [nix-instantiate](command-ref/nix-instantiate.md)
- [nix-prefetch-url](command-ref/nix-prefetch-url.md) - [nix-prefetch-url](command-ref/nix-prefetch-url.md)
- [Experimental Commands](command-ref/experimental-commands.md) - [Experimental Commands](command-ref/experimental-commands.md)
- [nix](command-ref/nix.md) @manpages@
- [Files](command-ref/files.md) - [Files](command-ref/files.md)
- [nix.conf](command-ref/conf-file.md) - [nix.conf](command-ref/conf-file.md)
- [Glossary](glossary.md) - [Glossary](glossary.md)
- [Hacking](hacking.md) - [Contributing](contributing/contributing.md)
- [Hacking](contributing/hacking.md)
- [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md) - [Release Notes](release-notes/release-notes.md)
- [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md) - [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md)
- [Release 2.2 (2019-01-11)](release-notes/rl-2.2.md) - [Release 2.2 (2019-01-11)](release-notes/rl-2.2.md)

View file

@ -53,7 +53,7 @@ set -f # disable globbing
export IFS=' ' export IFS=' '
echo "Signing paths" $OUT_PATHS echo "Signing paths" $OUT_PATHS
nix sign-paths --key-file /etc/nix/key.private $OUT_PATHS nix store sign --key-file /etc/nix/key.private $OUT_PATHS
echo "Uploading paths" $OUT_PATHS echo "Uploading paths" $OUT_PATHS
exec nix copy --to 's3://example-nix-cache' $OUT_PATHS exec nix copy --to 's3://example-nix-cache' $OUT_PATHS
``` ```
@ -63,7 +63,7 @@ exec nix copy --to 's3://example-nix-cache' $OUT_PATHS
> The `$OUT_PATHS` variable is a space-separated list of Nix store > The `$OUT_PATHS` variable is a space-separated list of Nix store
> paths. In this case, we expect and want the shell to perform word > paths. In this case, we expect and want the shell to perform word
> splitting to make each output path its own argument to `nix > splitting to make each output path its own argument to `nix
> sign-paths`. Nix guarantees the paths will not contain any spaces, > store sign`. Nix guarantees the paths will not contain any spaces,
> however a store path might contain glob characters. The `set -f` > however a store path might contain glob characters. The `set -f`
> disables globbing in the shell. > disables globbing in the shell.

View file

@ -19,19 +19,33 @@ By default Nix reads settings from the following places:
and `XDG_CONFIG_HOME`. If these are unset, it will look in and `XDG_CONFIG_HOME`. If these are unset, it will look in
`$HOME/.config/nix.conf`. `$HOME/.config/nix.conf`.
The configuration files consist of `name = - If `NIX_CONFIG` is set, its contents is treated as the contents of
value` pairs, one per line. Other files can be included with a line like a configuration file.
`include
path`, where *path* is interpreted relative to the current conf file and The configuration files consist of `name = value` pairs, one per
a missing file is an error unless `!include` is used instead. Comments line. Other files can be included with a line like `include path`,
where *path* is interpreted relative to the current conf file and a
missing file is an error unless `!include` is used instead. Comments
start with a `#` character. Here is an example configuration file: start with a `#` character. Here is an example configuration file:
keep-outputs = true # Nice for developers keep-outputs = true # Nice for developers
keep-derivations = true # Idem keep-derivations = true # Idem
You can override settings on the command line using the `--option` flag, You can override settings on the command line using the `--option`
e.g. `--option keep-outputs flag, e.g. `--option keep-outputs false`. Every configuration setting
false`. also has a corresponding command line flag, e.g. `--max-jobs 16`; for
Boolean settings, there are two flags to enable or disable the setting
(e.g. `--keep-failed` and `--no-keep-failed`).
A configuration setting usually overrides any previous value. However,
you can prefix the name of the setting by `extra-` to *append* to the
previous value. For instance,
substituters = a b
extra-substituters = c d
defines the `substituters` setting to be `a b c d`. This is also
available as a command line flag (e.g. `--extra-substituters`).
The following settings are currently available: The following settings are currently available:

View file

@ -81,6 +81,11 @@ Most Nix commands interpret the following environment variables:
Overrides the location of the system Nix configuration directory Overrides the location of the system Nix configuration directory
(default `prefix/etc/nix`). (default `prefix/etc/nix`).
- `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 Overrides the location of the user Nix configuration files to load
from (defaults to the XDG spec locations). The variable is treated from (defaults to the XDG spec locations). The variable is treated

View file

@ -45,7 +45,7 @@ md5sum`.
- `--type` *hashAlgo* - `--type` *hashAlgo*
Use the specified cryptographic hash algorithm, which can be one of Use the specified cryptographic hash algorithm, which can be one of
`md5`, `sha1`, and `sha256`. `md5`, `sha1`, `sha256`, and `sha512`.
- `--to-base16` - `--to-base16`
Dont hash anything, but convert the base-32 hash representation Dont hash anything, but convert the base-32 hash representation

View file

@ -39,7 +39,7 @@ Nix store is also printed.
- `--type` *hashAlgo* - `--type` *hashAlgo*
Use the specified cryptographic hash algorithm, which can be one of Use the specified cryptographic hash algorithm, which can be one of
`md5`, `sha1`, and `sha256`. `md5`, `sha1`, `sha256`, and `sha512`.
- `--print-path` - `--print-path`
Print the store path of the downloaded file on standard output. Print the store path of the downloaded file on standard output.

View file

@ -32,7 +32,7 @@ URL of a tarball that will be downloaded and unpacked to a temporary
location. The tarball must include a single top-level directory location. The tarball must include a single top-level directory
containing at least a file named `default.nix`. containing at least a file named `default.nix`.
If the derivation defines the variable `shellHook`, it will be evaluated If the derivation defines the variable `shellHook`, it will be run
after `$stdenv/setup` has been sourced. Since this hook is not executed after `$stdenv/setup` has been sourced. Since this hook is not executed
by regular Nix builds, it allows you to perform initialisation specific by regular Nix builds, it allows you to perform initialisation specific
to `nix-shell`. For example, the derivation attribute to `nix-shell`. For example, the derivation attribute
@ -41,10 +41,12 @@ to `nix-shell`. For example, the derivation attribute
shellHook = shellHook =
'' ''
echo "Hello shell" echo "Hello shell"
export SOME_API_TOKEN="$(cat ~/.config/some-app/api-token)"
''; '';
``` ```
will cause `nix-shell` to print `Hello shell`. will cause `nix-shell` to print `Hello shell` and set the `SOME_API_TOKEN`
environment variable to a user-configured value.
# Options # Options
@ -76,8 +78,8 @@ All options not listed here are passed to `nix-store
cleared before the interactive shell is started, so you get an cleared before the interactive shell is started, so you get an
environment that more closely corresponds to the “real” Nix build. A environment that more closely corresponds to the “real” Nix build. A
few variables, in particular `HOME`, `USER` and `DISPLAY`, are few variables, in particular `HOME`, `USER` and `DISPLAY`, are
retained. Note that `~/.bashrc` and (depending on your Bash retained. Note that (depending on your Bash
installation) `/etc/bashrc` are still sourced, so any variables set installation) `/etc/bashrc` is still sourced, so any variables set
there will affect the interactive shell. there will affect the interactive shell.
- `--packages` / `-p` *packages* - `--packages` / `-p` *packages*
@ -230,22 +232,23 @@ terraform apply
> in a nix-shell shebang. > in a nix-shell shebang.
Finally, using the merging of multiple nix-shell shebangs the following Finally, using the merging of multiple nix-shell shebangs the following
Haskell script uses a specific branch of Nixpkgs/NixOS (the 18.03 stable Haskell script uses a specific branch of Nixpkgs/NixOS (the 20.03 stable
branch): branch):
```haskell ```haskell
#! /usr/bin/env nix-shell #! /usr/bin/env nix-shell
#! nix-shell -i runghc -p "haskellPackages.ghcWithPackages (ps: [ps.HTTP ps.tagsoup])" #! nix-shell -i runghc -p "haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])"
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-18.03.tar.gz #! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-20.03.tar.gz
import Network.HTTP import Network.Curl.Download
import Text.HTML.TagSoup import Text.HTML.TagSoup
import Data.Either
import Data.ByteString.Char8 (unpack)
-- Fetch nixos.org and print all hrefs. -- Fetch nixos.org and print all hrefs.
main = do main = do
resp <- Network.HTTP.simpleHTTP (getRequest "http://nixos.org/") resp <- openURI "https://nixos.org/"
body <- getResponseBody resp let tags = filter (isTagOpenName "a") $ parseTags $ unpack $ fromRight undefined resp
let tags = filter (isTagOpenName "a") $ parseTags body
let tags' = map (fromAttrib "href") tags let tags' = map (fromAttrib "href") tags
mapM_ putStrLn $ filter (/= "") tags' mapM_ putStrLn $ filter (/= "") tags'
``` ```

View file

@ -226,7 +226,7 @@ control what gets deleted and in what order:
or TiB units. or TiB units.
The behaviour of the collector is also influenced by the The behaviour of the collector is also influenced by the
`keep-outputs` and `keep-derivations` variables in the Nix `keep-outputs` and `keep-derivations` settings in the Nix
configuration file. configuration file.
By default, the collector prints the total number of freed bytes when it By default, the collector prints the total number of freed bytes when it

View file

@ -0,0 +1,589 @@
# CLI guideline
## Goals
Purpose of this document is to provide a clear direction to **help design
delightful command line** experience. This document contain guidelines to
follow to ensure a consistent and approachable user experience.
## Overview
`nix` command provides a single entry to a number of sub-commands that help
**developers and system administrators** in the life-cycle of a software
project. We particularly need to pay special attention to help and assist new
users of Nix.
# Naming the `COMMANDS`
Words matter. Naming is an important part of the usability. Users will be
interacting with Nix on a regular basis so we should **name things for ease of
understanding**.
We recommend following the [Principle of Least
Astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment).
This means that you should **never use acronyms or abbreviations** unless they
are commonly used in other tools (e.g. `nix init`). And if the command name is
too long (> 10-12 characters) then shortening it makes sense (e.g.
“prioritization” → “priority”).
Commands should **follow a noun-verb dialogue**. Although noun-verb formatting
seems backwards from a speaking perspective (i.e. `nix store copy` vs. `nix
copy store`) it allows us to organize commands the same way users think about
completing an action (the group first, then the command).
## Naming rules
Rules are there to guide you by limiting your options. But not everything can
fit the rules all the time. In those cases document the exceptions in [Appendix
1: Commands naming exceptions](#appendix-1-commands-naming-exceptions) and
provide reason. The rules want to force a Nix developer to look, not just at
the command at hand, but also the command in a full context alongside other
`nix` commands.
```shell
$ nix [<GROUP>] <COMMAND> [<ARGUMENTS>] [<OPTIONS>]
```
- `GROUP`, `COMMAND`, `ARGUMENTS` and `OPTIONS` should be lowercase and in a
singular form.
- `GROUP` should be a **NOUN**.
- `COMMAND` should be a **VERB**.
- `ARGUMENTS` and `OPTIONS` are discussed in [*Input* section](#input).
## Classification
Some commands are more important, some less. While we want all of our commands
to be perfect we can only spend limited amount of time testing and improving
them.
This classification tries to separate commands in 3 categories in terms of
their importance in regards to the new users. Users who are likely to be
impacted the most by bad user experience.
- **Main commands**
Commands used for our main use cases and most likely used by new users. We
expect attention to details, such as:
- Proper use of [colors](#colors), [emojis](#special-unicode-characters)
and [aligning of text](#text-alignment).
- [Autocomplete](#shell-completion) of options.
- Show [next possible steps](#next-steps).
- Showing some [“tips”](#educate-the-user) when running logs running tasks
(eg. building / downloading) in order to teach users interesting bits of
Nix ecosystem.
- [Help pages](#help-is-essential) to be as good as we can write them
pointing to external documentation and tutorials for more.
Examples of such commands: `nix init`, `nix develop`, `nix build`, `nix run`,
...
- **Infrequently used commands**
From infrequently used commands we expect less attention to details, but
still some:
- Proper use of [colors](#colors), [emojis](#special-unicode-characters)
and [aligning of text](#text-alignment).
- [Autocomplete](#shell-completion) of options.
Examples of such commands: `nix doctor`, `nix edit`, `nix eval`, ...
- **Utility and scripting commands**
Commands that expose certain internal functionality of `nix`, mostly used by
other scripts.
- [Autocomplete](#shell-completion) of options.
Examples of such commands: `nix store copy`, `nix hash base16`, `nix store
ping`, ...
# Help is essential
Help should be built into your command line so that new users can gradually
discover new features when they need them.
## Looking for help
Since there is no standard way how user will look for help we rely on ways help
is provided by commonly used tools. As a guide for this we took `git` and
whenever in doubt look at it as a preferred direction.
The rules are:
- Help is shown by using `--help` or `help` command (eg `nix` `--``help` or
`nix help`).
- For non-COMMANDs (eg. `nix` `--``help` and `nix store` `--``help`) we **show
a summary** of most common use cases. Summary is presented on the STDOUT
without any use of PAGER.
- For COMMANDs (eg. `nix init` `--``help` or `nix help init`) we display the
man page of that command. By default the PAGER is used (as in `git`).
- At the end of either summary or man page there should be an URL pointing to
an online version of more detailed documentation.
- The structure of summaries and man pages should be the same as in `git`.
## Anticipate where help is needed
Even better then requiring the user to search for help is to anticipate and
predict when user might need it. Either because the lack of discoverability,
typo in the input or simply taking the opportunity to teach the user of
interesting - but less visible - details.
### Shell completion
This type of help is most common and almost expected by users. We need to
**provide the best shell completion** for `bash`, `zsh` and `fish`.
Completion needs to be **context aware**, this mean when a user types:
```shell
$ nix build n<TAB>
```
we need to display a list of flakes starting with `n`.
### Wrong input
As we all know we humans make mistakes, all the time. When a typo - intentional
or unintentional - is made, we should prompt for closest possible options or
point to the documentation which would educate user to not make the same
errors. Here are few examples:
In first example we prompt the user for typing wrong command name:
```shell
$ nix int
------------------------------------------------------------------------
Error! Command `int` not found.
------------------------------------------------------------------------
Did you mean:
|> nix init
|> nix input
```
Sometimes users will make mistake either because of a typo or simply because of
lack of discoverability. Our handling of this cases needs to be context
sensitive.
```shell
$ nix init --template=template#pyton
------------------------------------------------------------------------
Error! Template `template#pyton` not found.
------------------------------------------------------------------------
Initializing Nix project at `/path/to/here`.
Select a template for you new project:
|> template#pyton
template#python-pip
template#python-poetry
```
### Next steps
It can be invaluable to newcomers to show what a possible next steps and what
is the usual development workflow with Nix. For example:
```shell
$ nix init --template=template#python
Initializing project `template#python`
in `/home/USER/dev/new-project`
Next steps
|> nix develop -- to enter development environment
|> nix build -- to build your project
```
### Educate the user
We should take any opportunity to **educate users**, but at the same time we
must **be very very careful to not annoy users**. There is a thin line between
being helpful and being annoying.
An example of educating users might be to provide *Tips* in places where they
are waiting.
```shell
$ nix build
Started building my-project 1.2.3
Downloaded python3.8-poetry 1.2.3 in 5.3 seconds
Downloaded python3.8-requests 1.2.3 in 5.3 seconds
------------------------------------------------------------------------
Press `v` to increase logs verbosity
|> `?` to see other options
------------------------------------------------------------------------
Learn something new with every build...
|> See last logs of a build with `nix log --last` command.
------------------------------------------------------------------------
Evaluated my-project 1.2.3 in 14.43 seconds
Downloading [12 / 200]
|> firefox 1.2.3 [#########> ] 10Mb/s | 2min left
Building [2 / 20]
|> glibc 1.2.3 -> buildPhase: <last log line>
------------------------------------------------------------------------
```
Now **Learn** part of the output is where you educate users. You should only
show it when you know that a build will take some time and not annoy users of
the builds that take only few seconds.
Every feature like this should go though a intensive review and testing to
collect as much a feedback as possible and to fine tune every little detail. If
done right this can be an awesome features beginners and advance users will
love, but if not done perfectly it will annoy users and leave bad impression.
# Input
Input to a command is provided via `ARGUMENTS` and `OPTIONS`.
`ARGUMENTS` represent a required input for a function. When choosing to use
`ARGUMENT` over function please be aware of the downsides that come with it:
- User will need to remember the order of `ARGUMENTS`. This is not a problem if
there is only one `ARGUMENT`.
- With `OPTIONS` it is possible to provide much better auto completion.
- With `OPTIONS` it is possible to provide much better error message.
- Using `OPTIONS` it will mean there is a little bit more typing.
We dont discourage the use of `ARGUMENTS`, but simply want to make every
developer consider the downsides and choose wisely.
## Naming the `OPTIONS`
Then only naming convention - apart from the ones mentioned in Naming the
`COMMANDS` section is how flags are named.
Flags are a type of `OPTION` that represent an option that can be turned ON of
OFF. We can say **flags are boolean type of** `**OPTION**`.
Here are few examples of flag `OPTIONS`:
- `--colors` vs. `--no-colors` (showing colors in the output)
- `--emojis` vs. `--no-emojis` (showing emojis in the output)
## Prompt when input not provided
For *main commands* (as [per classification](#classification)) we want command
to improve the discoverability of possible input. A new user will most likely
not know which `ARGUMENTS` and `OPTIONS` are required or which values are
possible for those options.
In cases, the user might not provide the input or they provide wrong input,
rather then show the error, prompt a user with an option to find and select
correct input (see examples).
Prompting is of course not required when TTY is not attached to STDIN. This
would mean that scripts wont need to handle prompt, but rather handle errors.
A place to use prompt and provide user with interactive select
```shell
$ nix init
Initializing Nix project at `/path/to/here`.
Select a template for you new project:
|> py
template#python-pip
template#python-poetry
[ Showing 2 templates from 1345 templates ]
```
Another great place to add prompts are **confirmation dialogues for dangerous
actions**. For example when adding new substitutor via `OPTIONS` or via
`flake.nix` we should prompt - for the first time - and let user review what is
going to happen.
```shell
$ nix build --option substitutors https://cache.example.org
------------------------------------------------------------------------
Warning! A security related question need to be answered.
------------------------------------------------------------------------
The following substitutors will be used to in `my-project`:
- https://cache.example.org
Do you allow `my-project` to use above mentioned substitutors?
[y/N] |> y
```
# Output
Terminal output can be quite limiting in many ways. Which should forces us to
think about the experience even more. As with every design the output is a
compromise between being terse and being verbose, between showing help to
beginners and annoying advance users. For this it is important that we know
what are the priorities.
Nix command line should be first and foremost written with beginners in mind.
But users wont stay beginners for long and what was once useful might quickly
become annoying. There is no golden rule that we can give in this guideline
that would make it easier how to draw a line and find best compromise.
What we would encourage is to **build prototypes**, do some **user testing**
and collect **feedback**. Then repeat the cycle few times.
First design the *happy path* and only after your iron it out, continue to work
on **edge cases** (handling and displaying errors, changes of the output by
certain `OPTIONS`, etc…)
## Follow best practices
Needless to say we Nix must be a good citizen and follow best practices in
command line.
In short: **STDOUT is for output, STDERR is for (human) messaging.**
STDOUT and STDERR provide a way for you to output messages to the user while
also allowing them to redirect content to a file. For example:
```shell
$ nix build > build.txt
------------------------------------------------------------------------
Error! Atrribute `bin` missing at (1:94) from string.
------------------------------------------------------------------------
1| with import <nixpkgs> { }; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (surge.bin) ]; } ""
```
Because this warning is on STDERR, it doesnt end up in the file.
But not everything on STDERR is an error though. For example, you can run `nix
build` and collect logs in a file while still seeing the progress.
```
$ nix build > build.txt
Evaluated 1234 files in 1.2 seconds
Downloaded python3.8-poetry 1.2.3 in 5.3 seconds
Downloaded python3.8-requests 1.2.3 in 5.3 seconds
------------------------------------------------------------------------
Press `v` to increase logs verbosity
|> `?` to see other options
------------------------------------------------------------------------
Learn something new with every build...
|> See last logs of a build with `nix log --last` command.
------------------------------------------------------------------------
Evaluated my-project 1.2.3 in 14.43 seconds
Downloading [12 / 200]
|> firefox 1.2.3 [#########> ] 10Mb/s | 2min left
Building [2 / 20]
|> glibc 1.2.3 -> buildPhase: <last log line>
------------------------------------------------------------------------
```
## Errors (WIP)
**TODO**: Once we have implementation for the *happy path* then we will think
how to present errors.
## Not only for humans
Terse, machine-readable output formats can also be useful but shouldnt get in
the way of making beautiful CLI output. When needed, commands should offer a
`--json` flag to allow users to easily parse and script the CLI.
When TTY is not detected on STDOUT we should remove all design elements (no
colors, no emojis and using ASCII instead of Unicode symbols). The same should
happen when TTY is not detected on STDERR. We should not display progress /
status section, but only print warnings and errors.
## Dialog with the user
CLIs don't always make it clear when an action has taken place. For every
action a user performs, your CLI should provide an equal and appropriate
reaction, clearly highlighting the what just happened. For example:
```shell
$ nix build
Downloaded python3.8-poetry 1.2.3 in 5.3 seconds
Downloaded python3.8-requests 1.2.3 in 5.3 seconds
...
Success! You have successfully built my-project.
$
```
Above command clearly states that command successfully completed. And in case
of `nix build`, which is a command that might take some time to complete, it is
equally important to also show that a command started.
## Text alignment
Text alignment is the number one design element that will present all of the
Nix commands as a family and not as separate tools glued together.
The format we should follow is:
```shell
$ nix COMMAND
VERB_1 NOUN and other words
VERB__1 NOUN and other words
|> Some details
```
Few rules that we can extract from above example:
- Each line should start at least with one space.
- First word should be a VERB and must be aligned to the right.
- Second word should be a NOUN and must be aligned to the left.
- If you can not find a good VERB / NOUN pair, dont worry make it as
understandable to the user as possible.
- More details of each line can be provided by `|>` character which is serving
as the first word when aligning the text
Dont forget you should also test your terminal output with colors and emojis
off (`--no-colors --no-emojis`).
## Dim / Bright
After comparing few terminals with different color schemes we would **recommend
to avoid using dimmed text**. The difference from the rest of the text is very
little in many terminal and color scheme combinations. Sometimes the difference
is not even notable, therefore relying on it wouldnt make much sense.
**The bright text is much better supported** across terminals and color
schemes. Most of the time the difference is perceived as if the bright text
would be bold.
## Colors
Humans are already conditioned by society to attach certain meaning to certain
colors. While the meaning is not universal, a simple collection of colors is
used to represent basic emotions.
Colors that can be used in output
- Red = error, danger, stop
- Green = success, good
- Yellow/Orange = proceed with caution, warning, in progress
- Blue/Magenta = stability, calm
While colors are nice, when command line is used by machines (in automation
scripts) you want to remove the colors. There should be a global `--no-colors`
option that would remove the colors.
## Special (Unicode) characters
Most of the terminal have good support for Unicode characters and you should
use them in your output by default. But always have a backup solution that is
implemented only with ASCII characters and will be used when `--ascii` option
is going to be passed in. Please make sure that you test your output also
without Unicode characters
More they showing all the different Unicode characters it is important to
**establish common set of characters** that we use for certain situations.
## Emojis
Emojis help channel emotions even better than text, colors and special
characters.
We recommend **keeping the set of emojis to a minimum**. This will enable each
emoji to stand out more.
As not everybody is happy about emojis we should provide an `--no-emojis`
option to disable them. Please make sure that you test your output also without
emojis.
## Tables
All commands that are listing certain data can be implemented in some sort of a
table. Its important that each row of your output is a single entry of data.
Never output table borders. Its noisy and a huge pain for parsing using other
tools such as `grep`.
Be mindful of the screen width. Only show a few columns by default with the
table header, for more the table can be manipulated by the following options:
- `--no-headers`: Show column headers by default but allow to hide them.
- `--columns`: Comma-separated list of column names to add.
- `--sort`: Allow sorting by column. Allow inverse and multi-column sort as well.
## Interactive output
Interactive output was selected to be able to strike the balance between
beginners and advance users. While the default output will target beginners it
can, with a few key strokes, be changed into and advance introspection tool.
### Progress
For longer running commands we should provide and overview of the progress.
This is shown best in `nix build` example:
```shell
$ nix build
Started building my-project 1.2.3
Downloaded python3.8-poetry 1.2.3 in 5.3 seconds
Downloaded python3.8-requests 1.2.3 in 5.3 seconds
------------------------------------------------------------------------
Press `v` to increase logs verbosity
|> `?` to see other options
------------------------------------------------------------------------
Learn something new with every build...
|> See last logs of a build with `nix log --last` command.
------------------------------------------------------------------------
Evaluated my-project 1.2.3 in 14.43 seconds
Downloading [12 / 200]
|> firefox 1.2.3 [#########> ] 10Mb/s | 2min left
Building [2 / 20]
|> glibc 1.2.3 -> buildPhase: <last log line>
------------------------------------------------------------------------
```
### Search
Use a `fzf` like fuzzy search when there are multiple options to choose from.
```shell
$ nix init
Initializing Nix project at `/path/to/here`.
Select a template for you new project:
|> py
template#python-pip
template#python-poetry
[ Showing 2 templates from 1345 templates ]
```
### Prompt
In some situations we need to prompt the user and inform the user about what is
going to happen.
```shell
$ nix build --option substitutors https://cache.example.org
------------------------------------------------------------------------
Warning! A security related question need to be answered.
------------------------------------------------------------------------
The following substitutors will be used to in `my-project`:
- https://cache.example.org
Do you allow `my-project` to use above mentioned substitutors?
[y/N] |> y
```
## Verbosity
There are many ways that you can control verbosity.
Verbosity levels are:
- `ERROR` (level 0)
- `WARN` (level 1)
- `NOTICE` (level 2)
- `INFO` (level 3)
- `TALKATIVE` (level 4)
- `CHATTY` (level 5)
- `DEBUG` (level 6)
- `VOMIT` (level 7)
The default level that the command starts is `ERROR`. The simplest way to
increase the verbosity by stacking `-v` option (eg: `-vvv == level 3 == INFO`).
There are also two shortcuts, `--debug` to run in `DEBUG` verbosity level and
`--quiet` to run in `ERROR` verbosity level.
----------
# Appendix 1: Commands naming exceptions
`nix init` and `nix repl` are well established

View file

@ -0,0 +1 @@
# Contributing

View file

@ -25,5 +25,4 @@ order of precedence (from strongest to weakest binding).
| Inequality | *e1* `!=` *e2* | none | Inequality. | 11 | | Inequality | *e1* `!=` *e2* | none | Inequality. | 11 |
| Logical AND | *e1* `&&` *e2* | left | Logical AND. | 12 | | Logical AND | *e1* `&&` *e2* | left | Logical AND. | 12 |
| Logical OR | *e1* `\|\|` *e2* | left | Logical OR. | 13 | | Logical OR | *e1* `\|\|` *e2* | left | Logical OR. | 13 |
| Logical Implication | *e1* `->` *e2* | none | Logical implication (equivalent to `!e1 \|\| | Logical Implication | *e1* `->` *e2* | none | Logical implication (equivalent to `!e1 \|\| e2`). | 14 |
e2`). | 14 |

View file

@ -195,7 +195,7 @@ If you are comfortable navigating these tradeoffs, you can encrypt the
volume with something along the lines of: volume with something along the lines of:
```console ```console
alice$ diskutil apfs enableFileVault /nix -user disk $ diskutil apfs enableFileVault /nix -user disk
``` ```
## Symlink the Nix store to a custom location ## Symlink the Nix store to a custom location
@ -234,13 +234,13 @@ as a helpful reference if you run into trouble.
without a reboot: without a reboot:
```console ```console
alice$ /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B $ /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B
``` ```
3. Create the new APFS volume with diskutil: 3. Create the new APFS volume with diskutil:
```console ```console
alice$ sudo diskutil apfs addVolume diskX APFS 'Nix Store' -mountpoint /nix $ sudo diskutil apfs addVolume diskX APFS 'Nix Store' -mountpoint /nix
``` ```
4. Using `vifs`, add the new mount to `/etc/fstab`. If it doesn't 4. Using `vifs`, add the new mount to `/etc/fstab`. If it doesn't
@ -280,10 +280,10 @@ it somewhere (e.g. in `/tmp`), and then run the script named `install`
inside the binary tarball: inside the binary tarball:
```console ```console
alice$ cd /tmp $ cd /tmp
alice$ tar xfj nix-1.8-x86_64-darwin.tar.bz2 $ tar xfj nix-1.8-x86_64-darwin.tar.bz2
alice$ cd nix-1.8-x86_64-darwin $ cd nix-1.8-x86_64-darwin
alice$ ./install $ ./install
``` ```
If you need to edit the multi-user installation script to use different If you need to edit the multi-user installation script to use different

View file

@ -30,7 +30,7 @@
have bzip2 installed, including development headers and libraries. have bzip2 installed, including development headers and libraries.
If your distribution does not provide these, you can obtain bzip2 If your distribution does not provide these, you can obtain bzip2
from from
<https://web.archive.org/web/20180624184756/http://www.bzip.org/>. <https://sourceware.org/bzip2/>.
- `liblzma`, which is provided by XZ Utils. If your distribution does - `liblzma`, which is provided by XZ Utils. If your distribution does
not provide this, you can get it from <https://tukaani.org/xz/>. not provide this, you can get it from <https://tukaani.org/xz/>.

View file

@ -165,10 +165,10 @@ Youre then dropped into a shell where you can edit, build and test
the package: the package:
```console ```console
[nix-shell]$ tar xf $src [nix-shell]$ unpackPhase
[nix-shell]$ cd pan-* [nix-shell]$ cd pan-*
[nix-shell]$ ./configure [nix-shell]$ configurePhase
[nix-shell]$ make [nix-shell]$ buildPhase
[nix-shell]$ ./pan/gui/pan [nix-shell]$ ./pan/gui/pan
``` ```

View file

@ -0,0 +1,8 @@
# Release 2.4 (202X-XX-XX)
- It is now an error to modify the `plugin-files` setting via a
command-line flag that appears after the first non-flag argument
to any command, including a subcommand to `nix`. For example,
`nix-instantiate default.nix --plugin-files ""` must now become
`nix-instantiate --plugin-files "" default.nix`.
- Plugins that add new `nix` subcommands are now actually respected.

View file

@ -1,7 +1,15 @@
with builtins; with builtins;
{ rec {
splitLines = s: filter (x: !isList x) (split "\n" s); splitLines = s: filter (x: !isList x) (split "\n" s);
concatStrings = concatStringsSep ""; concatStrings = concatStringsSep "";
# FIXME: O(n^2)
unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [];
nameValuePair = name: value: { inherit name value; };
filterAttrs = pred: set:
listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
} }

View file

@ -1,39 +1,22 @@
{ {
"nodes": { "nodes": {
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1598695561,
"narHash": "sha256-gyH/5j+h/nWw0W8AcR2WKvNBUsiQ7QuxqSJNXAwV+8E=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "1705b4a26fbf065d9574dce47a94e8c7c79e052f",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1591633336, "lastModified": 1602702596,
"narHash": "sha256-oVXv4xAnDJB03LvZGbC72vSVlIbbJr8tpjEW5o/Fdek=", "narHash": "sha256-fqJ4UgOb4ZUnCDIapDb4gCrtAah5Rnr2/At3IzMitig=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "70717a337f7ae4e486ba71a500367cad697e5f09", "rev": "ad0d20345219790533ebe06571f82ed6b034db31",
"type": "github" "type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "id": "nixpkgs",
"ref": "nixos-20.03-small", "ref": "nixos-20.09-small",
"type": "indirect" "type": "indirect"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
} }
} }

241
flake.nix
View file

@ -1,10 +1,10 @@
{ {
description = "The purely functional package manager"; description = "The purely functional package manager";
inputs.nixpkgs.url = "nixpkgs/nixos-20.03-small"; inputs.nixpkgs.url = "nixpkgs/nixos-20.09-small";
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; }; #inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
outputs = { self, nixpkgs, lowdown-src }: outputs = { self, nixpkgs }:
let let
@ -12,11 +12,13 @@
versionSuffix = versionSuffix =
if officialRelease if officialRelease
then "" then ""
else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified)}_${self.shortRev or "dirty"}"; else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}_${self.shortRev or "dirty"}";
officialRelease = false; officialRelease = false;
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ]; linux64BitSystems = [ "x86_64-linux" "aarch64-linux" ];
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
systems = linuxSystems ++ [ "x86_64-darwin" ];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
@ -61,34 +63,42 @@
"LDFLAGS=-fuse-ld=gold" "LDFLAGS=-fuse-ld=gold"
]; ];
buildDeps =
[ bison
flex
mdbook
lowdown
autoconf-archive
autoreconfHook
curl nativeBuildDeps =
[
buildPackages.bison
buildPackages.flex
(lib.getBin buildPackages.lowdown)
buildPackages.mdbook
buildPackages.autoconf-archive
buildPackages.autoreconfHook
buildPackages.pkgconfig
# Tests
buildPackages.git
buildPackages.mercurial
buildPackages.jq
];
buildDeps =
[ curl
bzip2 xz brotli zlib editline bzip2 xz brotli zlib editline
openssl pkgconfig sqlite openssl sqlite
libarchive libarchive
boost boost
nlohmann_json nlohmann_json
lowdown
# Tests
git
mercurial
jq
gmock gmock
] ]
++ lib.optionals stdenv.isLinux [libseccomp utillinuxMinimal] ++ lib.optionals stdenv.isLinux [libseccomp utillinuxMinimal]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) ++ lib.optional stdenv.isx86_64 libcpuid;
(aws-sdk-cpp.override {
apis = ["s3" "transfer"]; awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
customMemoryManagement = false; (aws-sdk-cpp.override {
}); apis = ["s3" "transfer"];
customMemoryManagement = false;
});
propagatedDeps = propagatedDeps =
[ (boehmgc.override { enableLargeConfig = true; }) [ (boehmgc.override { enableLargeConfig = true; })
@ -100,14 +110,49 @@
]; ];
}; };
installScriptFor = systems:
with nixpkgsFor.x86_64-linux;
runCommand "installer-script"
{ buildInputs = [ nix ];
}
''
mkdir -p $out/nix-support
# Converts /nix/store/50p3qk8kka9dl6wyq40vydq945k0j3kv-nix-2.4pre20201102_550e11f/bin/nix
# To 50p3qk8kka9dl6wyq40vydq945k0j3kv/bin/nix
tarballPath() {
# Remove the store prefix
local path=''${1#${builtins.storeDir}/}
# Get the path relative to the derivation root
local rest=''${path#*/}
# Get the derivation hash
local drvHash=''${path%%-*}
echo "$drvHash/$rest"
}
substitute ${./scripts/install.in} $out/install \
${pkgs.lib.concatMapStrings
(system:
'' \
--replace '@tarballHash_${system}@' $(nix --experimental-features nix-command hash-file --base16 --type sha256 ${self.hydraJobs.binaryTarball.${system}}/*.tar.xz) \
--replace '@tarballPath_${system}@' $(tarballPath ${self.hydraJobs.binaryTarball.${system}}/*.tar.xz) \
''
)
systems
} --replace '@nixVersion@' ${version}
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
'';
in { in {
# A Nixpkgs overlay that overrides the 'nix' and # A Nixpkgs overlay that overrides the 'nix' and
# 'nix.perl-bindings' packages. # 'nix.perl-bindings' packages.
overlay = final: prev: { overlay = final: prev: {
nix = with final; with commonDeps pkgs; (stdenv.mkDerivation { nix = with final; with commonDeps pkgs; stdenv.mkDerivation {
name = "nix-${version}"; name = "nix-${version}";
inherit version;
src = self; src = self;
@ -115,7 +160,8 @@
outputs = [ "out" "dev" "doc" ]; outputs = [ "out" "dev" "doc" ];
buildInputs = buildDeps; nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ awsDeps;
propagatedBuildInputs = propagatedDeps; propagatedBuildInputs = propagatedDeps;
@ -152,21 +198,23 @@
installCheckFlags = "sysconfdir=$(out)/etc"; installCheckFlags = "sysconfdir=$(out)/etc";
separateDebugInfo = true; separateDebugInfo = true;
}) // {
perl-bindings = with final; stdenv.mkDerivation { passthru.perl-bindings = with final; stdenv.mkDerivation {
name = "nix-perl-${version}"; name = "nix-perl-${version}";
src = self; src = self;
nativeBuildInputs =
[ buildPackages.autoconf-archive
buildPackages.autoreconfHook
buildPackages.pkgconfig
];
buildInputs = buildInputs =
[ autoconf-archive [ nix
autoreconfHook
nix
curl curl
bzip2 bzip2
xz xz
pkgconfig
pkgs.perl pkgs.perl
boost boost
nlohmann_json nlohmann_json
@ -185,27 +233,25 @@
}; };
lowdown = with final; stdenv.mkDerivation { lowdown = with final; stdenv.mkDerivation rec {
name = "lowdown-0.7.1"; name = "lowdown-0.8.0";
/*
src = fetchurl { src = fetchurl {
url = https://kristaps.bsd.lv/lowdown/snapshots/lowdown-0.7.1.tar.gz; url = "https://kristaps.bsd.lv/lowdown/snapshots/${name}.tar.gz";
hash = "sha512-1daoAQfYD0LdhK6aFhrSQvadjc5GsSPBZw0fJDb+BEHYMBLjqiUl2A7H8N+l0W4YfGKqbsPYSrCy4vct+7U6FQ=="; hash = "sha512-U9WeGoInT9vrawwa57t6u9dEdRge4/P+0wLxmQyOL9nhzOEUU2FRz2Be9H0dCjYE7p2v3vCXIYk40M+jjULATw==";
}; };
*/
src = lowdown-src; #src = lowdown-src;
outputs = [ "out" "dev" ]; outputs = [ "out" "bin" "dev" ];
buildInputs = [ which ]; nativeBuildInputs = [ which ];
configurePhase = configurePhase =
'' ''
./configure \ ./configure \
PREFIX=${placeholder "dev"} \ PREFIX=${placeholder "dev"} \
BINDIR=${placeholder "out"}/bin BINDIR=${placeholder "bin"}/bin
''; '';
}; };
@ -214,10 +260,12 @@
hydraJobs = { hydraJobs = {
# Binary package for various platforms. # Binary package for various platforms.
build = nixpkgs.lib.genAttrs systems (system: nixpkgsFor.${system}.nix); build = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix);
buildStatic = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
# Perl bindings for various platforms. # Perl bindings for various platforms.
perlBindings = nixpkgs.lib.genAttrs systems (system: nixpkgsFor.${system}.nix.perl-bindings); perlBindings = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix.perl-bindings);
# Binary tarball for various platforms, containing a Nix store # Binary tarball for various platforms, containing a Nix store
# with the closure of 'nix' package, and the second half of # with the closure of 'nix' package, and the second half of
@ -236,6 +284,7 @@
} }
'' ''
cp ${installerClosureInfo}/registration $TMPDIR/reginfo cp ${installerClosureInfo}/registration $TMPDIR/reginfo
cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \ substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
--subst-var-by nix ${nix} \ --subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert} --subst-var-by cacert ${cacert}
@ -254,6 +303,7 @@
# SC1090: Don't worry about not being able to find # SC1090: Don't worry about not being able to find
# $nix/etc/profile.d/nix.sh # $nix/etc/profile.d/nix.sh
shellcheck --exclude SC1090 $TMPDIR/install shellcheck --exclude SC1090 $TMPDIR/install
shellcheck $TMPDIR/create-darwin-volume.sh
shellcheck $TMPDIR/install-darwin-multi-user.sh shellcheck $TMPDIR/install-darwin-multi-user.sh
shellcheck $TMPDIR/install-systemd-multi-user.sh shellcheck $TMPDIR/install-systemd-multi-user.sh
@ -269,6 +319,7 @@
fi fi
chmod +x $TMPDIR/install chmod +x $TMPDIR/install
chmod +x $TMPDIR/create-darwin-volume.sh
chmod +x $TMPDIR/install-darwin-multi-user.sh chmod +x $TMPDIR/install-darwin-multi-user.sh
chmod +x $TMPDIR/install-systemd-multi-user.sh chmod +x $TMPDIR/install-systemd-multi-user.sh
chmod +x $TMPDIR/install-multi-user chmod +x $TMPDIR/install-multi-user
@ -281,11 +332,15 @@
--absolute-names \ --absolute-names \
--hard-dereference \ --hard-dereference \
--transform "s,$TMPDIR/install,$dir/install," \ --transform "s,$TMPDIR/install,$dir/install," \
--transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \ --transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
--transform "s,$NIX_STORE,$dir/store,S" \ --transform "s,$NIX_STORE,$dir/store,S" \
$TMPDIR/install $TMPDIR/install-darwin-multi-user.sh \ $TMPDIR/install \
$TMPDIR/create-darwin-volume.sh \
$TMPDIR/install-darwin-multi-user.sh \
$TMPDIR/install-systemd-multi-user.sh \ $TMPDIR/install-systemd-multi-user.sh \
$TMPDIR/install-multi-user $TMPDIR/reginfo \ $TMPDIR/install-multi-user \
$TMPDIR/reginfo \
$(cat ${installerClosureInfo}/store-paths) $(cat ${installerClosureInfo}/store-paths)
''); '');
@ -293,23 +348,8 @@
# to https://nixos.org/nix/install. It downloads the binary # to https://nixos.org/nix/install. It downloads the binary
# tarball for the user's system and calls the second half of the # tarball for the user's system and calls the second half of the
# installation script. # installation script.
installerScript = installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ];
with nixpkgsFor.x86_64-linux; installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" ];
runCommand "installer-script"
{ buildInputs = [ nix ];
}
''
mkdir -p $out/nix-support
substitute ${./scripts/install.in} $out/install \
${pkgs.lib.concatMapStrings
(system: "--replace '@binaryTarball_${system}@' $(nix --experimental-features nix-command hash-file --base16 --type sha256 ${self.hydraJobs.binaryTarball.${system}}/*.tar.xz) ")
[ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ]
} \
--replace '@nixVersion@' ${version}
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
'';
# Line coverage analysis. # Line coverage analysis.
coverage = coverage =
@ -323,7 +363,8 @@
enableParallelBuilding = true; enableParallelBuilding = true;
buildInputs = buildDeps ++ propagatedDeps; nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ propagatedDeps ++ awsDeps;
dontInstall = false; dontInstall = false;
@ -364,38 +405,6 @@
inherit (self) overlay; inherit (self) overlay;
}); });
# Test whether the binary tarball works in an Ubuntu system.
tests.binaryTarball =
with nixpkgsFor.x86_64-linux;
vmTools.runInLinuxImage (runCommand "nix-binary-tarball-test"
{ diskImage = vmTools.diskImages.ubuntu1204x86_64;
}
''
set -x
useradd -m alice
su - alice -c 'tar xf ${self.hydraJobs.binaryTarball.x86_64-linux}/*.tar.*'
mkdir /dest-nix
mount -o bind /dest-nix /nix # Provide a writable /nix.
chown alice /nix
su - alice -c '_NIX_INSTALLER_TEST=1 ./nix-*/install'
su - alice -c 'nix-store --verify'
su - alice -c 'PAGER= nix-store -qR ${self.hydraJobs.build.x86_64-linux}'
# Check whether 'nix upgrade-nix' works.
cat > /tmp/paths.nix <<EOF
{
x86_64-linux = "${self.hydraJobs.build.x86_64-linux}";
}
EOF
su - alice -c 'nix --experimental-features nix-command upgrade-nix -vvv --nix-store-paths-url file:///tmp/paths.nix'
(! [ -L /home/alice/.profile-1-link ])
su - alice -c 'PAGER= nix-store -qR ${self.hydraJobs.build.x86_64-linux}'
mkdir -p $out/nix-support
touch $out/nix-support/hydra-build-products
umount /nix
'');
/* /*
# Check whether we can still evaluate all of Nixpkgs. # Check whether we can still evaluate all of Nixpkgs.
tests.evalNixpkgs = tests.evalNixpkgs =
@ -429,6 +438,43 @@
packages = forAllSystems (system: { packages = forAllSystems (system: {
inherit (nixpkgsFor.${system}) nix; inherit (nixpkgsFor.${system}) nix;
} // nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems) {
nix-static = let
nixpkgs = nixpkgsFor.${system}.pkgsStatic;
in with commonDeps nixpkgs; nixpkgs.stdenv.mkDerivation {
name = "nix-${version}";
src = self;
VERSION_SUFFIX = versionSuffix;
outputs = [ "out" "dev" "doc" ];
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ propagatedDeps;
configureFlags = [ "--sysconfdir=/etc" ];
enableParallelBuilding = true;
makeFlags = "profiledir=$(out)/etc/profile.d";
doCheck = true;
installFlags = "sysconfdir=$(out)/etc";
postInstall = ''
mkdir -p $doc/nix-support
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
mkdir -p $out/nix-support
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
'';
doInstallCheck = true;
installCheckFlags = "sysconfdir=$(out)/etc";
stripAllList = ["bin"];
};
}); });
defaultPackage = forAllSystems (system: self.packages.${system}.nix); defaultPackage = forAllSystems (system: self.packages.${system}.nix);
@ -442,7 +488,8 @@
outputs = [ "out" "dev" "doc" ]; outputs = [ "out" "dev" "doc" ];
buildInputs = buildDeps ++ propagatedDeps ++ perlDeps; nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ propagatedDeps ++ awsDeps ++ perlDeps;
inherit configureFlags; inherit configureFlags;

View file

@ -1,9 +1,3 @@
ifeq ($(MAKECMDGOALS), dist)
dist-files += $(shell cat .dist-files)
endif
dist-files += configure config.h.in perl/configure
clean-files += Makefile.config clean-files += Makefile.config
GLOBAL_CXXFLAGS += -Wno-deprecated-declarations GLOBAL_CXXFLAGS += -Wno-deprecated-declarations

View file

@ -2,4 +2,6 @@ ifeq ($(OS), Linux)
$(foreach n, nix-daemon.socket nix-daemon.service, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/systemd/system, 0644))) $(foreach n, nix-daemon.socket nix-daemon.service, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/systemd/system, 0644)))
clean-files += $(d)/nix-daemon.socket $(d)/nix-daemon.service
endif endif

View file

@ -2,4 +2,6 @@ ifeq ($(OS), Linux)
$(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(sysconfdir)/init, 0644))) $(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(sysconfdir)/init, 0644)))
clean-files += $(d)/nix-daemon.conf
endif endif

View file

@ -1,17 +0,0 @@
ifdef PACKAGE_NAME
dist-name = $(PACKAGE_NAME)-$(PACKAGE_VERSION)
dist: $(dist-name).tar.bz2 $(dist-name).tar.xz
$(dist-name).tar.bz2: $(dist-files)
$(trace-gen) tar cfj $@ $(sort $(dist-files)) --transform 's,^,$(dist-name)/,'
$(dist-name).tar.xz: $(dist-files)
$(trace-gen) tar cfJ $@ $(sort $(dist-files)) --transform 's,^,$(dist-name)/,'
clean-files += $(dist-name).tar.bz2 $(dist-name).tar.xz
print-top-help += echo " dist: Generate a source distribution";
endif

View file

@ -10,7 +10,6 @@ bin-scripts :=
noinst-scripts := noinst-scripts :=
man-pages := man-pages :=
install-tests := install-tests :=
dist-files :=
OS = $(shell uname -s) OS = $(shell uname -s)
@ -112,9 +111,6 @@ $(foreach test, $(install-tests), $(eval $(call run-install-test,$(test))))
$(foreach file, $(man-pages), $(eval $(call install-data-in, $(file), $(mandir)/man$(patsubst .%,%,$(suffix $(file)))))) $(foreach file, $(man-pages), $(eval $(call install-data-in, $(file), $(mandir)/man$(patsubst .%,%,$(suffix $(file))))))
include mk/dist.mk
.PHONY: default all man help .PHONY: default all man help
all: $(programs-list) $(libs-list) $(jars-list) $(man-pages) all: $(programs-list) $(libs-list) $(jars-list) $(man-pages)

View file

@ -159,5 +159,4 @@ define build-library
libs-list += $$($(1)_PATH) libs-list += $$($(1)_PATH)
endif endif
clean-files += $$(_d)/*.a $$(_d)/*.$(SO_EXT) $$(_d)/*.o $$(_d)/.*.dep $$($(1)_DEPS) $$($(1)_OBJS) clean-files += $$(_d)/*.a $$(_d)/*.$(SO_EXT) $$(_d)/*.o $$(_d)/.*.dep $$($(1)_DEPS) $$($(1)_OBJS)
dist-files += $$(_srcs)
endef endef

View file

@ -79,7 +79,6 @@ define build-program
programs-list += $$($(1)_PATH) programs-list += $$($(1)_PATH)
clean-files += $$($(1)_PATH) $$(_d)/*.o $$(_d)/.*.dep $$($(1)_DEPS) $$($(1)_OBJS) clean-files += $$($(1)_PATH) $$(_d)/*.o $$(_d)/.*.dep $$($(1)_DEPS) $$($(1)_OBJS)
dist-files += $$(_srcs)
# Phony target to run this program (typically as a dependency of 'check'). # Phony target to run this program (typically as a dependency of 'check').
.PHONY: $(1)_RUN .PHONY: $(1)_RUN

View file

@ -30,8 +30,6 @@ ifeq ($(OS), Darwin)
install_name_tool -id $@ $@ install_name_tool -id $@ $@
endif endif
dist-files += $(d)/vendor
clean: clean-rust clean: clean-rust
clean-rust: clean-rust:

View file

@ -2,7 +2,6 @@ CC = @CC@
CFLAGS = @CFLAGS@ CFLAGS = @CFLAGS@
CXX = @CXX@ CXX = @CXX@
CXXFLAGS = @CXXFLAGS@ CXXFLAGS = @CXXFLAGS@
HAVE_SODIUM = @HAVE_SODIUM@
PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION = @PACKAGE_VERSION@
SODIUM_LIBS = @SODIUM_LIBS@ SODIUM_LIBS = @SODIUM_LIBS@

View file

@ -40,11 +40,7 @@ AC_SUBST(perllibdir, [${libdir}/perl5/site_perl/$perlversion/$perlarchname])
AC_MSG_RESULT($perllibdir) AC_MSG_RESULT($perllibdir)
# Look for libsodium, an optional dependency. # Look for libsodium, an optional dependency.
PKG_CHECK_MODULES([SODIUM], [libsodium], PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
[AC_DEFINE([HAVE_SODIUM], [1], [Whether to use libsodium for cryptography.])
CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"
have_sodium=1], [have_sodium=])
AC_SUBST(HAVE_SODIUM, [$have_sodium])
# Check for the required Perl dependencies (DBI and DBD::SQLite). # Check for the required Perl dependencies (DBI and DBD::SQLite).
perlFlags="-I$perllibdir" perlFlags="-I$perllibdir"

View file

@ -14,9 +14,7 @@
#include "util.hh" #include "util.hh"
#include "crypto.hh" #include "crypto.hh"
#if HAVE_SODIUM
#include <sodium.h> #include <sodium.h>
#endif
using namespace nix; using namespace nix;
@ -110,10 +108,14 @@ SV * queryPathInfo(char * path, int base32)
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
mXPUSHi(info->registrationTime); mXPUSHi(info->registrationTime);
mXPUSHi(info->narSize); mXPUSHi(info->narSize);
AV * arr = newAV(); AV * refs = newAV();
for (auto & i : info->referencesPossiblyToSelf()) for (auto & i : info->referencesPossiblyToSelf())
av_push(arr, newSVpv(store()->printStorePath(i).c_str(), 0)); av_push(refs, newSVpv(store()->printStorePath(i).c_str(), 0));
XPUSHs(sv_2mortal(newRV((SV *) arr))); XPUSHs(sv_2mortal(newRV((SV *) refs)));
AV * sigs = newAV();
for (auto & i : info->sigs)
av_push(sigs, newSVpv(i.c_str(), 0));
XPUSHs(sv_2mortal(newRV((SV *) sigs)));
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); croak("%s", e.what());
} }
@ -235,12 +237,8 @@ SV * convertHash(char * algo, char * s, int toBase32)
SV * signString(char * secretKey_, char * msg) SV * signString(char * secretKey_, char * msg)
PPCODE: PPCODE:
try { try {
#if HAVE_SODIUM
auto sig = SecretKey(secretKey_).signDetached(msg); auto sig = SecretKey(secretKey_).signDetached(msg);
XPUSHs(sv_2mortal(newSVpv(sig.c_str(), sig.size()))); XPUSHs(sv_2mortal(newSVpv(sig.c_str(), sig.size())));
#else
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
#endif
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); croak("%s", e.what());
} }
@ -249,7 +247,6 @@ SV * signString(char * secretKey_, char * msg)
int checkSignature(SV * publicKey_, SV * sig_, char * msg) int checkSignature(SV * publicKey_, SV * sig_, char * msg)
CODE: CODE:
try { try {
#if HAVE_SODIUM
STRLEN publicKeyLen; STRLEN publicKeyLen;
unsigned char * publicKey = (unsigned char *) SvPV(publicKey_, publicKeyLen); unsigned char * publicKey = (unsigned char *) SvPV(publicKey_, publicKeyLen);
if (publicKeyLen != crypto_sign_PUBLICKEYBYTES) if (publicKeyLen != crypto_sign_PUBLICKEYBYTES)
@ -261,9 +258,6 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg)
throw Error("signature is not valid"); throw Error("signature is not valid");
RETVAL = crypto_sign_verify_detached(sig, (unsigned char *) msg, strlen(msg), publicKey) == 0; RETVAL = crypto_sign_verify_detached(sig, (unsigned char *) msg, strlen(msg), publicKey) == 0;
#else
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
#endif
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); croak("%s", e.what());
} }

View file

@ -5,42 +5,13 @@ root_disk() {
diskutil info -plist / diskutil info -plist /
} }
apfs_volumes_for() { # i.e., "disk1"
disk=$1 root_disk_identifier() {
diskutil apfs list -plist "$disk" diskutil info -plist / | xmllint --xpath "/plist/dict/key[text()='ParentWholeDisk']/following-sibling::string[1]/text()" -
}
disk_identifier() {
xpath "/plist/dict/key[text()='ParentWholeDisk']/following-sibling::string[1]/text()" 2>/dev/null
}
volume_list_true() {
key=$1
xpath "/plist/dict/array/dict/key[text()='Volumes']/following-sibling::array/dict/key[text()='$key']/following-sibling::true[1]" 2> /dev/null
}
volume_get_string() {
key=$1 i=$2
xpath "/plist/dict/array/dict/key[text()='Volumes']/following-sibling::array/dict[$i]/key[text()='$key']/following-sibling::string[1]/text()" 2> /dev/null
} }
find_nix_volume() { find_nix_volume() {
disk=$1 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
i=1
volumes=$(apfs_volumes_for "$disk")
while true; do
name=$(echo "$volumes" | volume_get_string "Name" "$i")
if [ -z "$name" ]; then
break
fi
case "$name" in
[Nn]ix*)
echo "$name"
break
;;
esac
i=$((i+1))
done
} }
test_fstab() { test_fstab() {
@ -55,6 +26,20 @@ test_synthetic_conf() {
grep -q "^nix$" /etc/synthetic.conf 2>/dev/null grep -q "^nix$" /etc/synthetic.conf 2>/dev/null
} }
# Create the paths defined in synthetic.conf, saving us a reboot.
create_synthetic_objects(){
# Big Sur takes away the -B flag we were using and replaces it
# with a -t flag that appears to do the same thing (but they
# don't behave exactly the same way in terms of return values).
# This feels a little dirty, but as far as I can tell the
# simplest way to get the right one is to just throw away stderr
# and call both... :]
{
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true # Big Sur
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B || true # Catalina
} >/dev/null 2>&1
}
test_nix() { test_nix() {
test -d "/nix" test -d "/nix"
} }
@ -89,9 +74,7 @@ test_t2_chip_present(){
} }
test_filevault_in_use() { test_filevault_in_use() {
disk=$1 fdesetup isactive >/dev/null
# list vols on disk | get value of Filevault key | value is true
apfs_volumes_for "$disk" | volume_list_true FileVault | grep -q true
} }
# use after error msg for conditions we don't understand # use after error msg for conditions we don't understand
@ -132,7 +115,7 @@ main() {
if ! test_nix; then if ! test_nix; then
echo "Creating mountpoint for /nix..." >&2 echo "Creating mountpoint for /nix..." >&2
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B || true create_synthetic_objects # the ones we defined in synthetic.conf
if ! test_nix; then if ! test_nix; then
sudo mkdir -p /nix 2>/dev/null || true sudo mkdir -p /nix 2>/dev/null || true
fi fi
@ -143,12 +126,12 @@ main() {
fi fi
fi fi
disk=$(root_disk | disk_identifier) disk="$(root_disk_identifier)"
volume=$(find_nix_volume "$disk") volume=$(find_nix_volume "$disk")
if [ -z "$volume" ]; then if [ -z "$volume" ]; then
echo "Creating a Nix Store volume..." >&2 echo "Creating a Nix Store volume..." >&2
if test_filevault_in_use "$disk"; then if test_filevault_in_use; then
# TODO: Not sure if it's in-scope now, but `diskutil apfs list` # TODO: Not sure if it's in-scope now, but `diskutil apfs list`
# shows both filevault and encrypted at rest status, and it # shows both filevault and encrypted at rest status, and it
# may be the more semantic way to test for this? It'll show # may be the more semantic way to test for this? It'll show
@ -178,6 +161,7 @@ main() {
if ! test_fstab; then if ! test_fstab; then
echo "Configuring /etc/fstab..." >&2 echo "Configuring /etc/fstab..." >&2
label=$(echo "$volume" | sed 's/ /\\040/g') 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 printf "\$a\nLABEL=%s /nix apfs rw,nobrowse\n.\nwq\n" "$label" | EDITOR=ed sudo vifs
fi fi
} }

View file

@ -37,6 +37,13 @@ poly_service_setup_note() {
EOF EOF
} }
poly_extra_try_me_commands(){
:
}
poly_extra_setup_instructions(){
:
}
poly_configure_nix_daemon_service() { poly_configure_nix_daemon_service() {
_sudo "to set up the nix-daemon as a LaunchDaemon" \ _sudo "to set up the nix-daemon as a LaunchDaemon" \
cp -f "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST" cp -f "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST"

View file

@ -71,11 +71,9 @@ uninstall_directions() {
subheader "Uninstalling nix:" subheader "Uninstalling nix:"
local step=0 local step=0
if [ -e /run/systemd/system ] && poly_service_installed_check; then if poly_service_installed_check; then
step=$((step + 1)) step=$((step + 1))
poly_service_uninstall_directions "$step" poly_service_uninstall_directions "$step"
else
step=$((step + 1))
fi fi
for profile_target in "${PROFILE_TARGETS[@]}"; do for profile_target in "${PROFILE_TARGETS[@]}"; do
@ -255,40 +253,20 @@ function finish_success {
echo "To try again later, run \"sudo -i nix-channel --update nixpkgs\"." echo "To try again later, run \"sudo -i nix-channel --update nixpkgs\"."
fi fi
if [ -e /run/systemd/system ]; then cat <<EOF
cat <<EOF
Before Nix will work in your existing shells, you'll need to close 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. them and open them again. Other than that, you should be ready to go.
Try it! Open a new terminal, and type: Try it! Open a new terminal, and type:
$(poly_extra_try_me_commands)
$ nix-shell -p nix-info --run "nix-info -m" $ 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 Thank you for using this installer. If you have any feedback, don't
hesitate: hesitate:
$(contactme) $(contactme)
EOF EOF
else
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:
$ sudo nix-daemon
$ nix-shell -p nix-info --run "nix-info -m"
Additionally, you may want to add nix-daemon to your init-system.
Thank you for using this installer. If you have any feedback, don't
hesitate:
$(contactme)
EOF
fi
} }
@ -630,24 +608,20 @@ EOF
} }
configure_shell_profile() { configure_shell_profile() {
# If there is an /etc/profile.d directory, we want to ensure there
# is a nix.sh within it, so we can use the following loop to add
# the source lines to it. Note that I'm _not_ adding the source
# lines here, because we want to be using the regular machinery.
#
# If we go around that machinery, it becomes more complicated and
# adds complications to the uninstall instruction generator and
# old instruction sniffer as well.
if [ -d /etc/profile.d ]; then
_sudo "create a stub /etc/profile.d/nix.sh which will be updated" \
touch /etc/profile.d/nix.sh
fi
for profile_target in "${PROFILE_TARGETS[@]}"; do for profile_target in "${PROFILE_TARGETS[@]}"; do
if [ -e "$profile_target" ]; then if [ -e "$profile_target" ]; then
_sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \ _sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \
cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX" cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX"
else
# try to create the file if its directory exists
target_dir="$(dirname "$profile_target")"
if [ -d "$target_dir" ]; then
_sudo "to create a stub $profile_target which will be updated" \
touch "$profile_target"
fi
fi
if [ -e "$profile_target" ]; then
shell_source_lines \ shell_source_lines \
| _sudo "extend your $profile_target with nix-daemon settings" \ | _sudo "extend your $profile_target with nix-daemon settings" \
tee -a "$profile_target" tee -a "$profile_target"
@ -725,9 +699,7 @@ main() {
setup_default_profile setup_default_profile
place_nix_configuration place_nix_configuration
if [ -e /run/systemd/system ]; then poly_configure_nix_daemon_service
poly_configure_nix_daemon_service
fi
trap finish_success EXIT trap finish_success EXIT
} }

View file

@ -26,9 +26,20 @@ fi
# macOS support for 10.12.6 or higher # macOS support for 10.12.6 or higher
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
macos_major=$(sw_vers -productVersion | cut -d '.' -f 2) IFS='.' read macos_major macos_minor macos_patch << EOF
macos_minor=$(sw_vers -productVersion | cut -d '.' -f 3) $(sw_vers -productVersion)
if [ "$macos_major" -lt 12 ] || { [ "$macos_major" -eq 12 ] && [ "$macos_minor" -lt 6 ]; }; then 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" echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.12.6 or higher"
exit 1 exit 1
fi fi
@ -87,10 +98,13 @@ while [ $# -gt 0 ]; do
echo "" echo ""
echo " --nix-extra-conf-file: Path to nix.conf to prepend when installing /etc/nix.conf" echo " --nix-extra-conf-file: Path to nix.conf to prepend when installing /etc/nix.conf"
echo "" echo ""
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+ # darwin and Catalina+
if [ "$(uname -s)" = "Darwin" ] && [ "$macos_major" -gt 14 ]; then 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 " --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 " store and mount it at /nix. This is the recommended way to create"
@ -110,8 +124,8 @@ if [ "$(uname -s)" = "Darwin" ]; then
"$self/create-darwin-volume.sh" "$self/create-darwin-volume.sh"
fi fi
info=$(diskutil info -plist / | xpath "/plist/dict/key[text()='Writable']/following-sibling::true[1]" 2> /dev/null) writable="$(diskutil info -plist / | xmllint --xpath "name(/plist/dict/key[text()='Writable']/following-sibling::*[1])" -)"
if ! [ -e $dest ] && [ -n "$info" ] && [ "$macos_major" -gt 14 ]; then if ! [ -e $dest ] && [ "$writable" = "false" ]; then
( (
echo "" echo ""
echo "Installing on macOS >=10.15 requires relocating the store to an apfs volume." echo "Installing on macOS >=10.15 requires relocating the store to an apfs volume."
@ -152,9 +166,15 @@ fi
mkdir -p $dest/store mkdir -p $dest/store
printf "copying Nix to %s..." "${dest}/store" >&2 printf "copying Nix to %s..." "${dest}/store" >&2
# Insert a newline if no progress is shown.
if [ ! -t 0 ]; then
echo ""
fi
for i in $(cd "$self/store" >/dev/null && echo ./*); do for i in $(cd "$self/store" >/dev/null && echo ./*); do
printf "." >&2 if [ -t 0 ]; then
printf "." >&2
fi
i_tmp="$dest/store/$i.$$" i_tmp="$dest/store/$i.$$"
if [ -e "$i_tmp" ]; then if [ -e "$i_tmp" ]; then
rm -rf "$i_tmp" rm -rf "$i_tmp"

View file

@ -72,24 +72,45 @@ poly_service_setup_note() {
EOF EOF
} }
poly_extra_try_me_commands(){
if [ -e /run/systemd/system ]; then
:
else
cat <<EOF
$ sudo nix-daemon
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() { poly_configure_nix_daemon_service() {
_sudo "to set up the nix-daemon service" \ if [ -e /run/systemd/system ]; then
systemctl link "/nix/var/nix/profiles/default$SERVICE_SRC" _sudo "to set up the nix-daemon service" \
systemctl link "/nix/var/nix/profiles/default$SERVICE_SRC"
_sudo "to set up the nix-daemon socket service" \ _sudo "to set up the nix-daemon socket service" \
systemctl enable "/nix/var/nix/profiles/default$SOCKET_SRC" systemctl enable "/nix/var/nix/profiles/default$SOCKET_SRC"
handle_network_proxy handle_network_proxy
_sudo "to load the systemd unit for nix-daemon" \ _sudo "to load the systemd unit for nix-daemon" \
systemctl daemon-reload systemctl daemon-reload
_sudo "to start the nix-daemon.socket" \ _sudo "to start the nix-daemon.socket" \
systemctl start nix-daemon.socket systemctl start nix-daemon.socket
_sudo "to start the nix-daemon.service" \
systemctl restart nix-daemon.service
_sudo "to start the nix-daemon.service" \
systemctl restart nix-daemon.service
fi
} }
poly_group_exists() { poly_group_exists() {

49
scripts/install.in Normal file → Executable file
View file

@ -25,16 +25,52 @@ require_util() {
} }
case "$(uname -s).$(uname -m)" in case "$(uname -s).$(uname -m)" in
Linux.x86_64) system=x86_64-linux; hash=@binaryTarball_x86_64-linux@;; Linux.x86_64)
Linux.i?86) system=i686-linux; hash=@binaryTarball_i686-linux@;; hash=@tarballHash_x86_64-linux@
Linux.aarch64) system=aarch64-linux; hash=@binaryTarball_aarch64-linux@;; path=@tarballPath_x86_64-linux@
Darwin.x86_64) system=x86_64-darwin; hash=@binaryTarball_x86_64-darwin@;; system=x86_64-linux
;;
Linux.i?86)
hash=@tarballHash_i686-linux@
path=@tarballPath_i686-linux@
system=i686-linux
;;
Linux.aarch64)
hash=@tarballHash_aarch64-linux@
path=@tarballPath_aarch64-linux@
system=aarch64-linux
;;
Darwin.x86_64)
hash=@tarballHash_x86_64-darwin@
path=@tarballPath_x86_64-darwin@
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
;;
*) oops "sorry, there is no binary distribution of Nix for your platform";; *) oops "sorry, there is no binary distribution of Nix for your platform";;
esac esac
url="https://releases.nixos.org/nix/nix-@nixVersion@/nix-@nixVersion@-$system.tar.xz" # Use this command-line option to fetch the tarballs using nar-serve or Cachix
if [ "${1:-}" = "--tarball-url-prefix" ]; then
if [ -z "${2:-}" ]; then
oops "missing argument for --tarball-url-prefix"
fi
url=${2}/${path}
shift 2
else
url=https://releases.nixos.org/nix/nix-@nixVersion@/nix-@nixVersion@-$system.tar.xz
fi
tarball="$tmpDir/$(basename "$tmpDir/nix-@nixVersion@-$system.tar.xz")" tarball=$tmpDir/nix-@nixVersion@-$system.tar.xz
require_util curl "download the binary tarball" require_util curl "download the binary tarball"
require_util tar "unpack the binary tarball" require_util tar "unpack the binary tarball"
@ -66,6 +102,7 @@ tar -xJf "$tarball" -C "$unpack" || oops "failed to unpack '$url'"
script=$(echo "$unpack"/*/install) script=$(echo "$unpack"/*/install)
[ -e "$script" ] || oops "installation script is missing from the binary tarball!" [ -e "$script" ] || oops "installation script is missing from the binary tarball!"
export INVOKED_FROM_INSTALL_IN=1
"$script" "$@" "$script" "$@"
} # End of wrapping } # End of wrapping

View file

@ -17,11 +17,21 @@ elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
export NIX_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt export NIX_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
else else
# Fall back to what is in the nix profiles, favouring whatever is defined last. # Fall back to what is in the nix profiles, favouring whatever is defined last.
for i in $NIX_PROFILES; do check_nix_profiles() {
if [ -e $i/etc/ssl/certs/ca-bundle.crt ]; then if [ "$ZSH_VERSION" ]; then
export NIX_SSL_CERT_FILE=$i/etc/ssl/certs/ca-bundle.crt # Zsh by default doesn't split words in unquoted parameter expansion.
# Set local_options for these options to be reverted at the end of the function
# and shwordsplit to force splitting words in $NIX_PROFILES below.
setopt local_options shwordsplit
fi fi
done for i in $NIX_PROFILES; do
if [ -e $i/etc/ssl/certs/ca-bundle.crt ]; then
export NIX_SSL_CERT_FILE=$i/etc/ssl/certs/ca-bundle.crt
fi
done
}
check_nix_profiles
unset -f check_nix_profiles
fi fi
export PATH="$HOME/.nix-profile/bin:@localstatedir@/nix/profiles/default/bin:$PATH" export PATH="$HOME/.nix-profile/bin:@localstatedir@/nix/profiles/default/bin:$PATH"

View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -e
script=$(nix-build -A outputs.hydraJobs.installerScriptForGHA --no-out-link)
installerHash=$(echo $script | cut -b12-43 -)
installerURL=https://$CACHIX_NAME.cachix.org/serve/$installerHash/install
echo "::set-output name=installerURL::$installerURL"

View file

@ -17,7 +17,7 @@
#include "store-api.hh" #include "store-api.hh"
#include "derivations.hh" #include "derivations.hh"
#include "local-store.hh" #include "local-store.hh"
#include "../nix/legacy.hh" #include "legacy.hh"
using namespace nix; using namespace nix;
using std::cin; using std::cin;
@ -53,6 +53,9 @@ static int main_build_remote(int argc, char * * argv)
unsetenv("DISPLAY"); unsetenv("DISPLAY");
unsetenv("SSH_ASKPASS"); unsetenv("SSH_ASKPASS");
/* If we ever use the common args framework, make sure to
remove initPlugins below and initialize settings first.
*/
if (argc != 2) if (argc != 2)
throw UsageError("called without required arguments"); throw UsageError("called without required arguments");
@ -71,11 +74,15 @@ static int main_build_remote(int argc, char * * argv)
initPlugins(); initPlugins();
auto store = openStore().cast<LocalStore>(); auto store = openStore();
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since /* It would be more appropriate to use $XDG_RUNTIME_DIR, since
that gets cleared on reboot, but it wouldn't work on macOS. */ that gets cleared on reboot, but it wouldn't work on macOS. */
currentLoad = store->stateDir + "/current-load"; auto currentLoadName = "/current-load";
if (auto localStore = store.dynamic_pointer_cast<LocalFSStore>())
currentLoad = std::string { localStore->stateDir } + currentLoadName;
else
currentLoad = settings.nixStateDir + currentLoadName;
std::shared_ptr<Store> sshStore; std::shared_ptr<Store> sshStore;
AutoCloseFD bestSlotLock; AutoCloseFD bestSlotLock;
@ -172,13 +179,14 @@ static int main_build_remote(int argc, char * * argv)
else else
{ {
// build the hint template. // build the hint template.
string hintstring = "derivation: %s\nrequired (system, features): (%s, %s)"; string errorText =
hintstring += "\n%s available machines:"; "Failed to find a machine for remote build!\n"
hintstring += "\n(systems, maxjobs, supportedFeatures, mandatoryFeatures)"; "derivation: %s\nrequired (system, features): (%s, %s)";
errorText += "\n%s available machines:";
errorText += "\n(systems, maxjobs, supportedFeatures, mandatoryFeatures)";
for (unsigned int i = 0; i < machines.size(); ++i) { for (unsigned int i = 0; i < machines.size(); ++i)
hintstring += "\n(%s, %s, %s, %s)"; errorText += "\n(%s, %s, %s, %s)";
}
// add the template values. // add the template values.
string drvstr; string drvstr;
@ -187,25 +195,21 @@ static int main_build_remote(int argc, char * * argv)
else else
drvstr = "<unknown>"; drvstr = "<unknown>";
auto hint = hintformat(hintstring); auto error = hintformat(errorText);
hint error
% drvstr % drvstr
% neededSystem % neededSystem
% concatStringsSep<StringSet>(", ", requiredFeatures) % concatStringsSep<StringSet>(", ", requiredFeatures)
% machines.size(); % machines.size();
for (auto & m : machines) { for (auto & m : machines)
hint % concatStringsSep<vector<string>>(", ", m.systemTypes) error
% m.maxJobs % concatStringsSep<vector<string>>(", ", m.systemTypes)
% concatStringsSep<StringSet>(", ", m.supportedFeatures) % m.maxJobs
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures); % concatStringsSep<StringSet>(", ", m.supportedFeatures)
} % concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
logErrorInfo(lvlInfo, { printMsg(canBuildLocally ? lvlChatty : lvlWarn, error);
.name = "Remote build",
.description = "Failed to find a machine for remote build!",
.hint = hint
});
std::cerr << "# decline\n"; std::cerr << "# decline\n";
} }
@ -230,12 +234,9 @@ static int main_build_remote(int argc, char * * argv)
} catch (std::exception & e) { } catch (std::exception & e) {
auto msg = chomp(drainFD(5, false)); auto msg = chomp(drainFD(5, false));
logError({ printError("cannot build on '%s': %s%s",
.name = "Remote build", bestMachine->storeUri, e.what(),
.hint = hintfmt("cannot build on '%s': %s%s", msg.empty() ? "" : ": " + msg);
bestMachine->storeUri, e.what(),
(msg.empty() ? "" : ": " + msg))
});
bestMachine->enabled = false; bestMachine->enabled = false;
continue; continue;
} }
@ -288,8 +289,9 @@ connected:
if (!missing.empty()) { if (!missing.empty()) {
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri)); Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri));
for (auto & i : missing) if (auto localStore = store.dynamic_pointer_cast<LocalStore>())
store->locksHeld.insert(store->printStorePath(i)); /* FIXME: ugly */ for (auto & i : missing)
localStore->locksHeld.insert(store->printStorePath(i)); /* FIXME: ugly */
copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs, NoSubstitute); copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs, NoSubstitute);
} }

View file

@ -11,11 +11,20 @@ extern char * * environ __attribute__((weak));
namespace nix { namespace nix {
Commands * RegisterCommand::commands = nullptr; RegisterCommand::Commands * RegisterCommand::commands = nullptr;
void NixMultiCommand::printHelp(const string & programName, std::ostream & out) nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & prefix)
{ {
MultiCommand::printHelp(programName, out); nix::Commands res;
for (auto & [name, command] : *RegisterCommand::commands)
if (name.size() == prefix.size() + 1) {
bool equal = true;
for (size_t i = 0; i < prefix.size(); ++i)
if (name[i] != prefix[i]) equal = false;
if (equal)
res.insert_or_assign(name[prefix.size()], command);
}
return res;
} }
nlohmann::json NixMultiCommand::toJSON() nlohmann::json NixMultiCommand::toJSON()
@ -45,57 +54,72 @@ void StoreCommand::run()
run(getStore()); run(getStore());
} }
StorePathsCommand::StorePathsCommand(bool recursive) RealisedPathsCommand::RealisedPathsCommand(bool recursive)
: recursive(recursive) : recursive(recursive)
{ {
if (recursive) if (recursive)
addFlag({ addFlag({
.longName = "no-recursive", .longName = "no-recursive",
.description = "apply operation to specified paths only", .description = "Apply operation to specified paths only.",
.category = installablesCategory,
.handler = {&this->recursive, false}, .handler = {&this->recursive, false},
}); });
else else
addFlag({ addFlag({
.longName = "recursive", .longName = "recursive",
.shortName = 'r', .shortName = 'r',
.description = "apply operation to closure of the specified paths", .description = "Apply operation to closure of the specified paths.",
.category = installablesCategory,
.handler = {&this->recursive, true}, .handler = {&this->recursive, true},
}); });
mkFlag(0, "all", "apply operation to the entire store", &all); addFlag({
.longName = "all",
.description = "Apply the operation to every store path.",
.category = installablesCategory,
.handler = {&all, true},
});
} }
void StorePathsCommand::run(ref<Store> store) void RealisedPathsCommand::run(ref<Store> store)
{ {
StorePaths storePaths; std::vector<RealisedPath> paths;
if (all) { if (all) {
if (installables.size()) if (installables.size())
throw UsageError("'--all' does not expect arguments"); throw UsageError("'--all' does not expect arguments");
// XXX: Only uses opaque paths, ignores all the realisations
for (auto & p : store->queryAllValidPaths()) for (auto & p : store->queryAllValidPaths())
storePaths.push_back(p); paths.push_back(p);
} } else {
auto pathSet = toRealisedPaths(store, realiseMode, operateOn, installables);
else {
for (auto & p : toStorePaths(store, realiseMode, operateOn, installables))
storePaths.push_back(p);
if (recursive) { if (recursive) {
StorePathSet closure; auto roots = std::move(pathSet);
store->computeFSClosure(StorePathSet(storePaths.begin(), storePaths.end()), closure, false, false); pathSet = {};
storePaths.clear(); RealisedPath::closure(*store, roots, pathSet);
for (auto & p : closure)
storePaths.push_back(p);
} }
for (auto & path : pathSet)
paths.push_back(path);
} }
run(store, std::move(paths));
}
StorePathsCommand::StorePathsCommand(bool recursive)
: RealisedPathsCommand(recursive)
{
}
void StorePathsCommand::run(ref<Store> store, std::vector<RealisedPath> paths)
{
StorePaths storePaths;
for (auto & p : paths)
storePaths.push_back(p.path());
run(store, std::move(storePaths)); run(store, std::move(storePaths));
} }
void StorePathCommand::run(ref<Store> store) void StorePathCommand::run(ref<Store> store, std::vector<StorePath> storePaths)
{ {
auto storePaths = toStorePaths(store, Realise::Nothing, operateOn, installables);
if (storePaths.size() != 1) if (storePaths.size() != 1)
throw UsageError("this command requires exactly one store path"); throw UsageError("this command requires exactly one store path");
@ -119,7 +143,7 @@ MixProfile::MixProfile()
{ {
addFlag({ addFlag({
.longName = "profile", .longName = "profile",
.description = "profile to update", .description = "The profile to update.",
.labels = {"path"}, .labels = {"path"},
.handler = {&profile}, .handler = {&profile},
.completer = completePath .completer = completePath
@ -176,14 +200,14 @@ MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
addFlag({ addFlag({
.longName = "ignore-environment", .longName = "ignore-environment",
.shortName = 'i', .shortName = 'i',
.description = "clear the entire environment (except those specified with --keep)", .description = "Clear the entire environment (except those specified with `--keep`).",
.handler = {&ignoreEnvironment, true}, .handler = {&ignoreEnvironment, true},
}); });
addFlag({ addFlag({
.longName = "keep", .longName = "keep",
.shortName = 'k', .shortName = 'k',
.description = "keep specified environment variable", .description = "Keep the environment variable *name*.",
.labels = {"name"}, .labels = {"name"},
.handler = {[&](std::string s) { keep.insert(s); }}, .handler = {[&](std::string s) { keep.insert(s); }},
}); });
@ -191,7 +215,7 @@ MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
addFlag({ addFlag({
.longName = "unset", .longName = "unset",
.shortName = 'u', .shortName = 'u',
.description = "unset specified environment variable", .description = "Unset the environment variable *name*.",
.labels = {"name"}, .labels = {"name"},
.handler = {[&](std::string s) { unset.insert(s); }}, .handler = {[&](std::string s) { unset.insert(s); }},
}); });

View file

@ -13,6 +13,8 @@ namespace nix {
extern std::string programPath; extern std::string programPath;
extern char * * savedArgv;
class EvalState; class EvalState;
struct Pos; struct Pos;
class Store; class Store;
@ -21,10 +23,10 @@ static constexpr Command::Category catSecondary = 100;
static constexpr Command::Category catUtility = 101; static constexpr Command::Category catUtility = 101;
static constexpr Command::Category catNixInstallation = 102; static constexpr Command::Category catNixInstallation = 102;
static constexpr auto installablesCategory = "Options that change the interpretation of installables";
struct NixMultiCommand : virtual MultiCommand, virtual Command struct NixMultiCommand : virtual MultiCommand, virtual Command
{ {
void printHelp(const string & programName, std::ostream & out) override;
nlohmann::json toJSON() override; nlohmann::json toJSON() override;
}; };
@ -139,7 +141,7 @@ private:
}; };
/* A command that operates on zero or more store paths. */ /* A command that operates on zero or more store paths. */
struct StorePathsCommand : public InstallablesCommand struct RealisedPathsCommand : public InstallablesCommand
{ {
private: private:
@ -152,44 +154,64 @@ protected:
public: public:
StorePathsCommand(bool recursive = false); RealisedPathsCommand(bool recursive = false);
using StoreCommand::run; using StoreCommand::run;
virtual void run(ref<Store> store, std::vector<StorePath> storePaths) = 0; virtual void run(ref<Store> store, std::vector<RealisedPath> paths) = 0;
void run(ref<Store> store) override; void run(ref<Store> store) override;
bool useDefaultInstallables() override { return !all; } bool useDefaultInstallables() override { return !all; }
}; };
/* A command that operates on exactly one store path. */ struct StorePathsCommand : public RealisedPathsCommand
struct StorePathCommand : public InstallablesCommand
{ {
using StoreCommand::run; StorePathsCommand(bool recursive = false);
using RealisedPathsCommand::run;
virtual void run(ref<Store> store, std::vector<StorePath> storePaths) = 0;
void run(ref<Store> store, std::vector<RealisedPath> paths) override;
};
/* A command that operates on exactly one store path. */
struct StorePathCommand : public StorePathsCommand
{
using StorePathsCommand::run;
virtual void run(ref<Store> store, const StorePath & storePath) = 0; virtual void run(ref<Store> store, const StorePath & storePath) = 0;
void run(ref<Store> store) override; void run(ref<Store> store, std::vector<StorePath> storePaths) override;
}; };
/* A helper class for registering commands globally. */ /* A helper class for registering commands globally. */
struct RegisterCommand struct RegisterCommand
{ {
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
static Commands * commands; static Commands * commands;
RegisterCommand(const std::string & name, RegisterCommand(std::vector<std::string> && name,
std::function<ref<Command>()> command) std::function<ref<Command>()> command)
{ {
if (!commands) commands = new Commands; if (!commands) commands = new Commands;
commands->emplace(name, command); commands->emplace(name, command);
} }
static nix::Commands getCommandsFor(const std::vector<std::string> & prefix);
}; };
template<class T> template<class T>
static RegisterCommand registerCommand(const std::string & name) static RegisterCommand registerCommand(const std::string & name)
{ {
return RegisterCommand(name, [](){ return make_ref<T>(); }); return RegisterCommand({name}, [](){ return make_ref<T>(); });
}
template<class T>
static RegisterCommand registerCommand2(std::vector<std::string> && name)
{
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
} }
Buildables build(ref<Store> store, Realise mode, Buildables build(ref<Store> store, Realise mode,
@ -207,6 +229,12 @@ std::set<StorePath> toDerivations(ref<Store> store,
std::vector<std::shared_ptr<Installable>> installables, std::vector<std::shared_ptr<Installable>> installables,
bool useDeriver = false); bool useDeriver = false);
std::set<RealisedPath> toRealisedPaths(
ref<Store> store,
Realise mode,
OperateOn operateOn,
std::vector<std::shared_ptr<Installable>> installables);
/* Helper function to generate args that invoke $EDITOR on /* Helper function to generate args that invoke $EDITOR on
filename:lineno. */ filename:lineno. */
Strings editorFor(const Pos & pos); Strings editorFor(const Pos & pos);
@ -252,6 +280,8 @@ void completeFlakeRefWithFragment(
const Strings & defaultFlakeAttrPaths, const Strings & defaultFlakeAttrPaths,
std::string_view prefix); std::string_view prefix);
std::string showVersions(const std::set<std::string> & versions);
void printClosureDiff( void printClosureDiff(
ref<Store> store, ref<Store> store,
const StorePath & beforePath, const StorePath & beforePath,

View file

@ -16,8 +16,35 @@
#include <regex> #include <regex>
#include <queue> #include <queue>
#include <nlohmann/json.hpp>
namespace nix { 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( void completeFlakeInputPath(
ref<EvalState> evalState, ref<EvalState> evalState,
const FlakeRef & flakeRef, const FlakeRef & flakeRef,
@ -31,39 +58,47 @@ void completeFlakeInputPath(
MixFlakeOptions::MixFlakeOptions() MixFlakeOptions::MixFlakeOptions()
{ {
auto category = "Common flake-related options";
addFlag({ addFlag({
.longName = "recreate-lock-file", .longName = "recreate-lock-file",
.description = "recreate lock file from scratch", .description = "Recreate the flake's lock file from scratch.",
.category = category,
.handler = {&lockFlags.recreateLockFile, true} .handler = {&lockFlags.recreateLockFile, true}
}); });
addFlag({ addFlag({
.longName = "no-update-lock-file", .longName = "no-update-lock-file",
.description = "do not allow any updates to the lock file", .description = "Do not allow any updates to the flake's lock file.",
.category = category,
.handler = {&lockFlags.updateLockFile, false} .handler = {&lockFlags.updateLockFile, false}
}); });
addFlag({ addFlag({
.longName = "no-write-lock-file", .longName = "no-write-lock-file",
.description = "do not write the newly generated lock file", .description = "Do not write the flake's newly generated lock file.",
.category = category,
.handler = {&lockFlags.writeLockFile, false} .handler = {&lockFlags.writeLockFile, false}
}); });
addFlag({ addFlag({
.longName = "no-registries", .longName = "no-registries",
.description = "don't use flake registries", .description = "Don't allow lookups in the flake registries.",
.category = category,
.handler = {&lockFlags.useRegistries, false} .handler = {&lockFlags.useRegistries, false}
}); });
addFlag({ addFlag({
.longName = "commit-lock-file", .longName = "commit-lock-file",
.description = "commit changes to the lock file", .description = "Commit changes to the flake's lock file.",
.category = category,
.handler = {&lockFlags.commitLockFile, true} .handler = {&lockFlags.commitLockFile, true}
}); });
addFlag({ addFlag({
.longName = "update-input", .longName = "update-input",
.description = "update a specific flake input", .description = "Update a specific flake input (ignoring its previous entry in the lock file).",
.category = category,
.labels = {"input-path"}, .labels = {"input-path"},
.handler = {[&](std::string s) { .handler = {[&](std::string s) {
lockFlags.inputUpdates.insert(flake::parseInputPath(s)); lockFlags.inputUpdates.insert(flake::parseInputPath(s));
@ -76,7 +111,8 @@ MixFlakeOptions::MixFlakeOptions()
addFlag({ addFlag({
.longName = "override-input", .longName = "override-input",
.description = "override a specific flake input (e.g. `dwarffs/nixpkgs`)", .description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`).",
.category = category,
.labels = {"input-path", "flake-url"}, .labels = {"input-path", "flake-url"},
.handler = {[&](std::string inputPath, std::string flakeRef) { .handler = {[&](std::string inputPath, std::string flakeRef) {
lockFlags.inputOverrides.insert_or_assign( lockFlags.inputOverrides.insert_or_assign(
@ -87,7 +123,8 @@ MixFlakeOptions::MixFlakeOptions()
addFlag({ addFlag({
.longName = "inputs-from", .longName = "inputs-from",
.description = "use the inputs of the specified flake as registry entries", .description = "Use the inputs of the specified flake as registry entries.",
.category = category,
.labels = {"flake-url"}, .labels = {"flake-url"},
.handler = {[&](std::string flakeRef) { .handler = {[&](std::string flakeRef) {
auto evalState = getEvalState(); auto evalState = getEvalState();
@ -116,22 +153,25 @@ SourceExprCommand::SourceExprCommand()
addFlag({ addFlag({
.longName = "file", .longName = "file",
.shortName = 'f', .shortName = 'f',
.description = "evaluate *file* rather than the default", .description = "Interpret installables as attribute paths relative to the Nix expression stored in *file*.",
.category = installablesCategory,
.labels = {"file"}, .labels = {"file"},
.handler = {&file}, .handler = {&file},
.completer = completePath .completer = completePath
}); });
addFlag({ addFlag({
.longName ="expr", .longName = "expr",
.description = "evaluate attributes from *expr*", .description = "Interpret installables as attribute paths relative to the Nix expression *expr*.",
.category = installablesCategory,
.labels = {"expr"}, .labels = {"expr"},
.handler = {&expr} .handler = {&expr}
}); });
addFlag({ addFlag({
.longName ="derivation", .longName = "derivation",
.description = "operate on the store derivation rather than its outputs", .description = "Operate on the store derivation rather than its outputs.",
.category = installablesCategory,
.handler = {&operateOn, OperateOn::Derivation}, .handler = {&operateOn, OperateOn::Derivation},
}); });
} }
@ -382,7 +422,7 @@ std::vector<InstallableValue::DerivationInfo> InstallableAttrPath::toDerivations
for (auto & drvInfo : drvInfos) { for (auto & drvInfo : drvInfos) {
res.push_back({ res.push_back({
state->store->parseStorePath(drvInfo.queryDrvPath()), state->store->parseStorePath(drvInfo.queryDrvPath()),
state->store->parseStorePath(drvInfo.queryOutPath()), state->store->maybeParseStorePath(drvInfo.queryOutPath()),
drvInfo.queryOutputName() drvInfo.queryOutputName()
}); });
} }
@ -456,6 +496,23 @@ static std::string showAttrPaths(const std::vector<std::string> & paths)
return s; return s;
} }
InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
Strings && attrPaths,
Strings && prefixes,
const flake::LockFlags & lockFlags)
: InstallableValue(state),
flakeRef(flakeRef),
attrPaths(attrPaths),
prefixes(prefixes),
lockFlags(lockFlags)
{
if (cmd && cmd->getAutoArgs(*state)->size())
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
}
std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation() std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
{ {
auto lockedFlake = getLockedFlake(); auto lockedFlake = getLockedFlake();
@ -474,7 +531,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
auto drvInfo = DerivationInfo{ auto drvInfo = DerivationInfo{
std::move(drvPath), std::move(drvPath),
state->store->parseStorePath(attr->getAttr(state->sOutPath)->getString()), state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()),
attr->getAttr(state->sOutputName)->getString() attr->getAttr(state->sOutputName)->getString()
}; };
@ -533,8 +590,11 @@ InstallableFlake::getCursors(EvalState & state)
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{ {
if (!_lockedFlake) if (!_lockedFlake) {
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags)); _lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags));
_lockedFlake->flake.config.apply();
// FIXME: send new config to the daemon.
}
return _lockedFlake; return _lockedFlake;
} }
@ -585,9 +645,12 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
try { try {
auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath(".")); auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath("."));
result.push_back(std::make_shared<InstallableFlake>( result.push_back(std::make_shared<InstallableFlake>(
getEvalState(), std::move(flakeRef), this,
getEvalState(),
std::move(flakeRef),
fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment},
getDefaultFlakeAttrPathPrefixes(), lockFlags)); getDefaultFlakeAttrPathPrefixes(),
lockFlags));
continue; continue;
} catch (...) { } catch (...) {
ex = std::current_exception(); ex = std::current_exception();
@ -661,23 +724,42 @@ Buildables build(ref<Store> store, Realise mode,
return buildables; return buildables;
} }
StorePathSet toStorePaths(ref<Store> store, std::set<RealisedPath> toRealisedPaths(
Realise mode, OperateOn operateOn, ref<Store> store,
Realise mode,
OperateOn operateOn,
std::vector<std::shared_ptr<Installable>> installables) std::vector<std::shared_ptr<Installable>> installables)
{ {
StorePathSet outPaths; std::set<RealisedPath> res;
if (operateOn == OperateOn::Output) { if (operateOn == OperateOn::Output) {
for (auto & b : build(store, mode, installables)) for (auto & b : build(store, mode, installables))
std::visit(overloaded { std::visit(overloaded {
[&](BuildableOpaque bo) { [&](BuildableOpaque bo) {
outPaths.insert(bo.path); res.insert(bo.path);
}, },
[&](BuildableFromDrv bfd) { [&](BuildableFromDrv bfd) {
auto drv = store->readDerivation(bfd.drvPath);
auto outputHashes = staticOutputHashes(*store, drv);
for (auto & output : bfd.outputs) { for (auto & output : bfd.outputs) {
if (!output.second) if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
throw Error("Cannot operate on output of unbuilt CA drv"); if (!outputHashes.count(output.first))
outPaths.insert(*output.second); 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); }, b);
@ -688,9 +770,19 @@ StorePathSet toStorePaths(ref<Store> store,
for (auto & i : installables) for (auto & i : installables)
for (auto & b : i->toBuildables()) for (auto & b : i->toBuildables())
if (auto bfd = std::get_if<BuildableFromDrv>(&b)) if (auto bfd = std::get_if<BuildableFromDrv>(&b))
outPaths.insert(bfd->drvPath); res.insert(bfd->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());
return outPaths; return outPaths;
} }

View file

@ -7,6 +7,8 @@
#include <optional> #include <optional>
#include <nlohmann/json_fwd.hpp>
namespace nix { namespace nix {
struct DrvInfo; struct DrvInfo;
@ -16,11 +18,13 @@ namespace eval_cache { class EvalCache; class AttrCursor; }
struct BuildableOpaque { struct BuildableOpaque {
StorePath path; StorePath path;
nlohmann::json toJSON(ref<Store> store) const;
}; };
struct BuildableFromDrv { struct BuildableFromDrv {
StorePath drvPath; StorePath drvPath;
std::map<std::string, std::optional<StorePath>> outputs; std::map<std::string, std::optional<StorePath>> outputs;
nlohmann::json toJSON(ref<Store> store) const;
}; };
typedef std::variant< typedef std::variant<
@ -29,6 +33,7 @@ typedef std::variant<
> Buildable; > Buildable;
typedef std::vector<Buildable> Buildables; typedef std::vector<Buildable> Buildables;
nlohmann::json buildablesToJSON(const Buildables & buildables, ref<Store> store);
struct App struct App
{ {
@ -99,11 +104,13 @@ struct InstallableFlake : InstallableValue
const flake::LockFlags & lockFlags; const flake::LockFlags & lockFlags;
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake; mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
InstallableFlake(ref<EvalState> state, FlakeRef && flakeRef, InstallableFlake(
Strings && attrPaths, Strings && prefixes, const flake::LockFlags & lockFlags) SourceExprCommand * cmd,
: InstallableValue(state), flakeRef(flakeRef), attrPaths(attrPaths), ref<EvalState> state,
prefixes(prefixes), lockFlags(lockFlags) FlakeRef && flakeRef,
{ } Strings && attrPaths,
Strings && prefixes,
const flake::LockFlags & lockFlags);
std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); } std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }

15
src/libcmd/local.mk Normal file
View file

@ -0,0 +1,15 @@
libraries += libcmd
libcmd_NAME = libnixcmd
libcmd_DIR := $(d)
libcmd_SOURCES := $(wildcard $(d)/*.cc)
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
libcmd_LDFLAGS = -llowdown
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
$(eval $(call install-file-in, $(d)/nix-cmd.pc, $(prefix)/lib/pkgconfig, 0644))

View file

@ -3,9 +3,7 @@
#include "finally.hh" #include "finally.hh"
#include <sys/queue.h> #include <sys/queue.h>
extern "C" {
#include <lowdown.h> #include <lowdown.h>
}
namespace nix { namespace nix {
@ -42,7 +40,9 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
throw Error("cannot allocate Markdown output buffer"); throw Error("cannot allocate Markdown output buffer");
Finally freeBuffer([&]() { lowdown_buf_free(buf); }); Finally freeBuffer([&]() { lowdown_buf_free(buf); });
lowdown_term_rndr(buf, nullptr, renderer, node); int rndr_res = lowdown_term_rndr(buf, nullptr, renderer, node);
if (!rndr_res)
throw Error("allocation error while rendering Markdown");
return std::string(buf->data, buf->size); return std::string(buf->data, buf->size);
} }

9
src/libcmd/nix-cmd.pc.in Normal file
View file

@ -0,0 +1,9 @@
prefix=@prefix@
libdir=@libdir@
includedir=@includedir@
Name: Nix
Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lnixcmd
Cflags: -I${includedir}/nix -std=c++17

View file

@ -52,9 +52,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
for (auto & attr : tokens) { for (auto & attr : tokens) {
/* Is i an index (integer) or a normal attribute name? */ /* Is i an index (integer) or a normal attribute name? */
enum { apAttr, apIndex } apType = apAttr; auto attrIndex = string2Int<unsigned int>(attr);
unsigned int attrIndex;
if (string2Int(attr, attrIndex)) apType = apIndex;
/* Evaluate the expression. */ /* Evaluate the expression. */
Value * vNew = state.allocValue(); Value * vNew = state.allocValue();
@ -65,9 +63,9 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
/* It should evaluate to either a set or an expression, /* It should evaluate to either a set or an expression,
according to what is specified in the attrPath. */ according to what is specified in the attrPath. */
if (apType == apAttr) { if (!attrIndex) {
if (v->type != tAttrs) if (v->type() != nAttrs)
throw TypeError( throw TypeError(
"the expression selected by the selection path '%1%' should be a set but is %2%", "the expression selected by the selection path '%1%' should be a set but is %2%",
attrPath, attrPath,
@ -82,17 +80,17 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
pos = *a->pos; pos = *a->pos;
} }
else if (apType == apIndex) { else {
if (!v->isList()) if (!v->isList())
throw TypeError( throw TypeError(
"the expression selected by the selection path '%1%' should be a list but is %2%", "the expression selected by the selection path '%1%' should be a list but is %2%",
attrPath, attrPath,
showType(*v)); showType(*v));
if (attrIndex >= v->listSize()) if (*attrIndex >= v->listSize())
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", attrIndex, attrPath); throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);
v = v->listElems()[attrIndex]; v = v->listElems()[*attrIndex];
pos = noPos; pos = noPos;
} }

View file

@ -24,9 +24,7 @@ void EvalState::mkAttrs(Value & v, size_t capacity)
v = vEmptySet; v = vEmptySet;
return; return;
} }
clearValue(v); v.mkAttrs(allocBindings(capacity));
v.type = tAttrs;
v.attrs = allocBindings(capacity);
nrAttrsets++; nrAttrsets++;
nrAttrsInAttrsets += capacity; nrAttrsInAttrsets += capacity;
} }

View file

@ -77,7 +77,7 @@ public:
auto a = get(name); auto a = get(name);
if (!a) if (!a)
throw Error({ throw Error({
.hint = hintfmt("attribute '%s' missing", name), .msg = hintfmt("attribute '%s' missing", name),
.errPos = pos .errPos = pos
}); });

View file

@ -12,16 +12,20 @@ namespace nix {
MixEvalArgs::MixEvalArgs() MixEvalArgs::MixEvalArgs()
{ {
auto category = "Common evaluation options";
addFlag({ addFlag({
.longName = "arg", .longName = "arg",
.description = "argument to be passed to Nix functions", .description = "Pass the value *expr* as the argument *name* to Nix functions.",
.category = category,
.labels = {"name", "expr"}, .labels = {"name", "expr"},
.handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }} .handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }}
}); });
addFlag({ addFlag({
.longName = "argstr", .longName = "argstr",
.description = "string-valued argument to be passed to Nix functions", .description = "Pass the string *string* as the argument *name* to Nix functions.",
.category = category,
.labels = {"name", "string"}, .labels = {"name", "string"},
.handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }}, .handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }},
}); });
@ -29,14 +33,16 @@ MixEvalArgs::MixEvalArgs()
addFlag({ addFlag({
.longName = "include", .longName = "include",
.shortName = 'I', .shortName = 'I',
.description = "add a path to the list of locations used to look up `<...>` file names", .description = "Add *path* to the list of locations used to look up `<...>` file names.",
.category = category,
.labels = {"path"}, .labels = {"path"},
.handler = {[&](std::string s) { searchPath.push_back(s); }} .handler = {[&](std::string s) { searchPath.push_back(s); }}
}); });
addFlag({ addFlag({
.longName = "impure", .longName = "impure",
.description = "allow access to mutable paths and repositories", .description = "Allow access to mutable paths and repositories.",
.category = category,
.handler = {[&]() { .handler = {[&]() {
evalSettings.pureEval = false; evalSettings.pureEval = false;
}}, }},
@ -44,7 +50,8 @@ MixEvalArgs::MixEvalArgs()
addFlag({ addFlag({
.longName = "override-flake", .longName = "override-flake",
.description = "override a flake registry value", .description = "Override the flake registries, redirecting *original-ref* to *resolved-ref*.",
.category = category,
.labels = {"original-ref", "resolved-ref"}, .labels = {"original-ref", "resolved-ref"},
.handler = {[&](std::string _from, std::string _to) { .handler = {[&](std::string _from, std::string _to) {
auto from = parseFlakeRef(_from, absPath(".")); auto from = parseFlakeRef(_from, absPath("."));

View file

@ -390,14 +390,14 @@ Value & AttrCursor::forceValue()
} }
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) { if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
if (v.type == tString) if (v.type() == nString)
cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context), cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context),
string_t{v.string.s, {}}}; string_t{v.string.s, {}}};
else if (v.type == tPath) else if (v.type() == nPath)
cachedValue = {root->db->setString(getKey(), v.path), v.path}; cachedValue = {root->db->setString(getKey(), v.path), string_t{v.path, {}}};
else if (v.type == tBool) else if (v.type() == nBool)
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean}; cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
else if (v.type == tAttrs) else if (v.type() == nAttrs)
; // FIXME: do something? ; // FIXME: do something?
else else
cachedValue = {root->db->setMisc(getKey()), misc_t()}; cachedValue = {root->db->setMisc(getKey()), misc_t()};
@ -442,7 +442,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
auto & v = forceValue(); auto & v = forceValue();
if (v.type != tAttrs) if (v.type() != nAttrs)
return nullptr; return nullptr;
//throw TypeError("'%s' is not an attribute set", getAttrPathStr()); //throw TypeError("'%s' is not an attribute set", getAttrPathStr());
@ -512,10 +512,10 @@ std::string AttrCursor::getString()
auto & v = forceValue(); auto & v = forceValue();
if (v.type != tString && v.type != tPath) if (v.type() != nString && v.type() != nPath)
throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type)); throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()));
return v.type == tString ? v.string.s : v.path; return v.type() == nString ? v.string.s : v.path;
} }
string_t AttrCursor::getStringWithContext() string_t AttrCursor::getStringWithContext()
@ -525,8 +525,17 @@ string_t AttrCursor::getStringWithContext()
cachedValue = root->db->getAttr(getKey(), root->state.symbols); cachedValue = root->db->getAttr(getKey(), root->state.symbols);
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) { if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto s = std::get_if<string_t>(&cachedValue->second)) { if (auto s = std::get_if<string_t>(&cachedValue->second)) {
debug("using cached string attribute '%s'", getAttrPathStr()); bool valid = true;
return *s; for (auto & c : s->second) {
if (!root->state.store->isValidPath(root->state.store->parseStorePath(c.first))) {
valid = false;
break;
}
}
if (valid) {
debug("using cached string attribute '%s'", getAttrPathStr());
return *s;
}
} else } else
throw TypeError("'%s' is not a string", getAttrPathStr()); throw TypeError("'%s' is not a string", getAttrPathStr());
} }
@ -534,12 +543,12 @@ string_t AttrCursor::getStringWithContext()
auto & v = forceValue(); auto & v = forceValue();
if (v.type == tString) if (v.type() == nString)
return {v.string.s, v.getContext()}; return {v.string.s, v.getContext()};
else if (v.type == tPath) else if (v.type() == nPath)
return {v.path, {}}; return {v.path, {}};
else else
throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type)); throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()));
} }
bool AttrCursor::getBool() bool AttrCursor::getBool()
@ -558,7 +567,7 @@ bool AttrCursor::getBool()
auto & v = forceValue(); auto & v = forceValue();
if (v.type != tBool) if (v.type() != nBool)
throw TypeError("'%s' is not a Boolean", getAttrPathStr()); throw TypeError("'%s' is not a Boolean", getAttrPathStr());
return v.boolean; return v.boolean;
@ -580,7 +589,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
auto & v = forceValue(); auto & v = forceValue();
if (v.type != tAttrs) if (v.type() != nAttrs)
throw TypeError("'%s' is not an attribute set", getAttrPathStr()); throw TypeError("'%s' is not an attribute set", getAttrPathStr());
std::vector<Symbol> attrs; std::vector<Symbol> attrs;

View file

@ -10,7 +10,7 @@ namespace nix {
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s)) LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
{ {
throw EvalError({ throw EvalError({
.hint = hintfmt(s), .msg = hintfmt(s),
.errPos = pos .errPos = pos
}); });
} }
@ -24,7 +24,7 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v)) LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
{ {
throw TypeError({ throw TypeError({
.hint = hintfmt(s, showType(v)), .msg = hintfmt(s, showType(v)),
.errPos = pos .errPos = pos
}); });
} }
@ -32,23 +32,21 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
void EvalState::forceValue(Value & v, const Pos & pos) void EvalState::forceValue(Value & v, const Pos & pos)
{ {
if (v.type == tThunk) { if (v.isThunk()) {
Env * env = v.thunk.env; Env * env = v.thunk.env;
Expr * expr = v.thunk.expr; Expr * expr = v.thunk.expr;
try { try {
v.type = tBlackhole; v.mkBlackhole();
//checkInterrupt(); //checkInterrupt();
expr->eval(*this, *env, v); expr->eval(*this, *env, v);
} catch (...) { } catch (...) {
v.type = tThunk; v.mkThunk(env, expr);
v.thunk.env = env;
v.thunk.expr = expr;
throw; throw;
} }
} }
else if (v.type == tApp) else if (v.isApp())
callFunction(*v.app.left, *v.app.right, v, noPos); callFunction(*v.app.left, *v.app.right, v, noPos);
else if (v.type == tBlackhole) else if (v.isBlackhole())
throwEvalError(pos, "infinite recursion encountered"); throwEvalError(pos, "infinite recursion encountered");
} }
@ -56,7 +54,7 @@ void EvalState::forceValue(Value & v, const Pos & pos)
inline void EvalState::forceAttrs(Value & v) inline void EvalState::forceAttrs(Value & v)
{ {
forceValue(v); forceValue(v);
if (v.type != tAttrs) if (v.type() != nAttrs)
throwTypeError("value is %1% while a set was expected", v); throwTypeError("value is %1% while a set was expected", v);
} }
@ -64,7 +62,7 @@ inline void EvalState::forceAttrs(Value & v)
inline void EvalState::forceAttrs(Value & v, const Pos & pos) inline void EvalState::forceAttrs(Value & v, const Pos & pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type != tAttrs) if (v.type() != nAttrs)
throwTypeError(pos, "value is %1% while a set was expected", v); throwTypeError(pos, "value is %1% while a set was expected", v);
} }

View file

@ -27,6 +27,10 @@
#include <gc/gc.h> #include <gc/gc.h>
#include <gc/gc_cpp.h> #include <gc/gc_cpp.h>
#include <boost/coroutine2/coroutine.hpp>
#include <boost/coroutine2/protected_fixedsize_stack.hpp>
#include <boost/context/stack_context.hpp>
#endif #endif
namespace nix { namespace nix {
@ -64,7 +68,7 @@ RootValue allocRootValue(Value * v)
} }
static void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v) void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
{ {
checkInterrupt(); checkInterrupt();
@ -73,7 +77,7 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
return; return;
} }
switch (v.type) { switch (v.internalType) {
case tInt: case tInt:
str << v.integer; str << v.integer;
break; break;
@ -154,32 +158,27 @@ std::ostream & operator << (std::ostream & str, const Value & v)
const Value *getPrimOp(const Value &v) { const Value *getPrimOp(const Value &v) {
const Value * primOp = &v; const Value * primOp = &v;
while (primOp->type == tPrimOpApp) { while (primOp->isPrimOpApp()) {
primOp = primOp->primOpApp.left; primOp = primOp->primOpApp.left;
} }
assert(primOp->type == tPrimOp); assert(primOp->isPrimOp());
return primOp; return primOp;
} }
string showType(ValueType type) string showType(ValueType type)
{ {
switch (type) { switch (type) {
case tInt: return "an integer"; case nInt: return "an integer";
case tBool: return "a Boolean"; case nBool: return "a Boolean";
case tString: return "a string"; case nString: return "a string";
case tPath: return "a path"; case nPath: return "a path";
case tNull: return "null"; case nNull: return "null";
case tAttrs: return "a set"; case nAttrs: return "a set";
case tList1: case tList2: case tListN: return "a list"; case nList: return "a list";
case tThunk: return "a thunk"; case nFunction: return "a function";
case tApp: return "a function application"; case nExternal: return "an external value";
case tLambda: return "a function"; case nFloat: return "a float";
case tBlackhole: return "a black hole"; case nThunk: return "a thunk";
case tPrimOp: return "a built-in function";
case tPrimOpApp: return "a partially applied built-in function";
case tExternal: return "an external value";
case tFloat: return "a float";
} }
abort(); abort();
} }
@ -187,15 +186,18 @@ string showType(ValueType type)
string showType(const Value & v) string showType(const Value & v)
{ {
switch (v.type) { switch (v.internalType) {
case tString: return v.string.context ? "a string with context" : "a string"; case tString: return v.string.context ? "a string with context" : "a string";
case tPrimOp: case tPrimOp:
return fmt("the built-in function '%s'", string(v.primOp->name)); return fmt("the built-in function '%s'", string(v.primOp->name));
case tPrimOpApp: case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name)); return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
case tExternal: return v.external->showType(); case tExternal: return v.external->showType();
case tThunk: return "a thunk";
case tApp: return "a function application";
case tBlackhole: return "a black hole";
default: default:
return showType(v.type); return showType(v.type());
} }
} }
@ -203,12 +205,13 @@ string showType(const Value & v)
bool Value::isTrivial() const bool Value::isTrivial() const
{ {
return return
type != tApp internalType != tApp
&& type != tPrimOpApp && internalType != tPrimOpApp
&& (type != tThunk && (internalType != tThunk
|| (dynamic_cast<ExprAttrs *>(thunk.expr) || (dynamic_cast<ExprAttrs *>(thunk.expr)
&& ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty()) && ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty())
|| dynamic_cast<ExprLambda *>(thunk.expr)); || dynamic_cast<ExprLambda *>(thunk.expr)
|| dynamic_cast<ExprList *>(thunk.expr));
} }
@ -219,6 +222,31 @@ static void * oomHandler(size_t requested)
/* Convert this to a proper C++ exception. */ /* Convert this to a proper C++ exception. */
throw std::bad_alloc(); throw std::bad_alloc();
} }
class BoehmGCStackAllocator : public StackAllocator {
boost::coroutines2::protected_fixedsize_stack stack {
// We allocate 8 MB, the default max stack size on NixOS.
// A smaller stack might be quicker to allocate but reduces the stack
// depth available for source filter expressions etc.
std::max(boost::context::stack_traits::default_size(), static_cast<std::size_t>(8 * 1024 * 1024))
};
public:
boost::context::stack_context allocate() override {
auto sctx = stack.allocate();
GC_add_roots(static_cast<char *>(sctx.sp) - sctx.size, sctx.sp);
return sctx;
}
void deallocate(boost::context::stack_context sctx) override {
GC_remove_roots(static_cast<char *>(sctx.sp) - sctx.size, sctx.sp);
stack.deallocate(sctx);
}
};
static BoehmGCStackAllocator boehmGCStackAllocator;
#endif #endif
@ -256,6 +284,8 @@ void initGC()
GC_set_oom_fn(oomHandler); GC_set_oom_fn(oomHandler);
StackAllocator::defaultAllocator = &boehmGCStackAllocator;
/* Set the initial heap size to something fairly big (25% of /* Set the initial heap size to something fairly big (25% of
physical RAM, up to a maximum of 384 MiB) so that in most cases physical RAM, up to a maximum of 384 MiB) so that in most cases
we don't need to garbage collect at all. (Collection has a we don't need to garbage collect at all. (Collection has a
@ -372,11 +402,6 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
for (auto & i : evalSettings.nixPath.get()) addToSearchPath(i); for (auto & i : evalSettings.nixPath.get()) addToSearchPath(i);
} }
try {
addToSearchPath("nix=" + canonPath(settings.nixDataDir + "/nix/corepkgs", true));
} catch (Error &) {
}
if (evalSettings.restrictEval || evalSettings.pureEval) { if (evalSettings.restrictEval || evalSettings.pureEval) {
allowedPaths = PathSet(); allowedPaths = PathSet();
@ -400,9 +425,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
} }
} }
clearValue(vEmptySet); vEmptySet.mkAttrs(allocBindings(0));
vEmptySet.type = tAttrs;
vEmptySet.attrs = allocBindings(0);
createBaseEnv(); createBaseEnv();
} }
@ -429,6 +452,8 @@ Path EvalState::checkSourcePath(const Path & path_)
*/ */
Path abspath = canonPath(path_); Path abspath = canonPath(path_);
if (hasPrefix(abspath, corepkgsPrefix)) return abspath;
for (auto & i : *allowedPaths) { for (auto & i : *allowedPaths) {
if (isDirOrInDir(abspath, i)) { if (isDirOrInDir(abspath, i)) {
found = true; found = true;
@ -518,16 +543,14 @@ Value * EvalState::addPrimOp(const string & name,
the primop to a dummy value. */ the primop to a dummy value. */
if (arity == 0) { if (arity == 0) {
auto vPrimOp = allocValue(); auto vPrimOp = allocValue();
vPrimOp->type = tPrimOp; vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym });
vPrimOp->primOp = new PrimOp { .fun = primOp, .arity = 1, .name = sym };
Value v; Value v;
mkApp(v, *vPrimOp, *vPrimOp); mkApp(v, *vPrimOp, *vPrimOp);
return addConstant(name, v); return addConstant(name, v);
} }
Value * v = allocValue(); Value * v = allocValue();
v->type = tPrimOp; v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = sym });
v->primOp = new PrimOp { .fun = primOp, .arity = arity, .name = sym };
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(sym, v)); baseEnv.values[0]->attrs->push_back(Attr(sym, v));
@ -542,8 +565,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
if (primOp.arity == 0) { if (primOp.arity == 0) {
primOp.arity = 1; primOp.arity = 1;
auto vPrimOp = allocValue(); auto vPrimOp = allocValue();
vPrimOp->type = tPrimOp; vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
vPrimOp->primOp = new PrimOp(std::move(primOp));
Value v; Value v;
mkApp(v, *vPrimOp, *vPrimOp); mkApp(v, *vPrimOp, *vPrimOp);
return addConstant(primOp.name, v); return addConstant(primOp.name, v);
@ -554,8 +576,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
primOp.name = symbols.create(std::string(primOp.name, 2)); primOp.name = symbols.create(std::string(primOp.name, 2));
Value * v = allocValue(); Value * v = allocValue();
v->type = tPrimOp; v->mkPrimOp(new PrimOp(std::move(primOp)));
v->primOp = new PrimOp(std::move(primOp));
staticBaseEnv.vars[envName] = baseEnvDispl; staticBaseEnv.vars[envName] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v)); baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v));
@ -571,9 +592,9 @@ Value & EvalState::getBuiltin(const string & name)
std::optional<EvalState::Doc> EvalState::getDoc(Value & v) std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
{ {
if (v.type == tPrimOp || v.type == tPrimOpApp) { if (v.isPrimOp() || v.isPrimOpApp()) {
auto v2 = &v; auto v2 = &v;
while (v2->type == tPrimOpApp) while (v2->isPrimOpApp())
v2 = v2->primOpApp.left; v2 = v2->primOpApp.left;
if (v2->primOp->doc) if (v2->primOp->doc)
return Doc { return Doc {
@ -601,7 +622,7 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2)) LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2))
{ {
throw EvalError({ throw EvalError({
.hint = hintfmt(s, s2), .msg = hintfmt(s, s2),
.errPos = pos .errPos = pos
}); });
} }
@ -614,7 +635,7 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, con
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3)) LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3))
{ {
throw EvalError({ throw EvalError({
.hint = hintfmt(s, s2, s3), .msg = hintfmt(s, s2, s3),
.errPos = pos .errPos = pos
}); });
} }
@ -623,7 +644,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const
{ {
// p1 is where the error occurred; p2 is a position mentioned in the message. // p1 is where the error occurred; p2 is a position mentioned in the message.
throw EvalError({ throw EvalError({
.hint = hintfmt(s, sym, p2), .msg = hintfmt(s, sym, p2),
.errPos = p1 .errPos = p1
}); });
} }
@ -631,20 +652,15 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s)) LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
{ {
throw TypeError({ throw TypeError({
.hint = hintfmt(s), .msg = hintfmt(s),
.errPos = pos .errPos = pos
}); });
} }
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
{
throw TypeError(s, s1);
}
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2)) LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
{ {
throw TypeError({ throw TypeError({
.hint = hintfmt(s, fun.showNamePos(), s2), .msg = hintfmt(s, fun.showNamePos(), s2),
.errPos = pos .errPos = pos
}); });
} }
@ -652,7 +668,7 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1)) LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
{ {
throw AssertionError({ throw AssertionError({
.hint = hintfmt(s, s1), .msg = hintfmt(s, s1),
.errPos = pos .errPos = pos
}); });
} }
@ -660,7 +676,15 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s,
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1)) LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1))
{ {
throw UndefinedVarError({ throw UndefinedVarError({
.hint = hintfmt(s, s1), .msg = hintfmt(s, s1),
.errPos = pos
});
}
LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1))
{
throw MissingArgumentError({
.msg = hintfmt(s, s1),
.errPos = pos .errPos = pos
}); });
} }
@ -678,15 +702,13 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con
void mkString(Value & v, const char * s) void mkString(Value & v, const char * s)
{ {
mkStringNoCopy(v, dupString(s)); v.mkString(dupString(s));
} }
Value & mkString(Value & v, std::string_view s, const PathSet & context) Value & mkString(Value & v, std::string_view s, const PathSet & context)
{ {
v.type = tString; v.mkString(dupStringWithLen(s.data(), s.size()));
v.string.s = dupStringWithLen(s.data(), s.size());
v.string.context = 0;
if (!context.empty()) { if (!context.empty()) {
size_t n = 0; size_t n = 0;
v.string.context = (const char * *) v.string.context = (const char * *)
@ -701,7 +723,7 @@ Value & mkString(Value & v, std::string_view s, const PathSet & context)
void mkPath(Value & v, const char * s) void mkPath(Value & v, const char * s)
{ {
mkPathNoCopy(v, dupString(s)); v.mkPath(dupString(s));
} }
@ -762,16 +784,9 @@ Env & EvalState::allocEnv(size_t size)
void EvalState::mkList(Value & v, size_t size) void EvalState::mkList(Value & v, size_t size)
{ {
clearValue(v); v.mkList(size);
if (size == 1) if (size > 2)
v.type = tList1; v.bigList.elems = (Value * *) allocBytes(size * sizeof(Value *));
else if (size == 2)
v.type = tList2;
else {
v.type = tListN;
v.bigList.size = size;
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
}
nrListElems += size; nrListElems += size;
} }
@ -780,9 +795,7 @@ unsigned long nrThunks = 0;
static inline void mkThunk(Value & v, Env & env, Expr * expr) static inline void mkThunk(Value & v, Env & env, Expr * expr)
{ {
v.type = tThunk; v.mkThunk(&env, expr);
v.thunk.env = &env;
v.thunk.expr = expr;
nrThunks++; nrThunks++;
} }
@ -917,7 +930,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
{ {
Value v; Value v;
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type != tBool) if (v.type() != nBool)
throwTypeError("value is %1% while a Boolean was expected", v); throwTypeError("value is %1% while a Boolean was expected", v);
return v.boolean; return v.boolean;
} }
@ -927,7 +940,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
{ {
Value v; Value v;
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type != tBool) if (v.type() != nBool)
throwTypeError(pos, "value is %1% while a Boolean was expected", v); throwTypeError(pos, "value is %1% while a Boolean was expected", v);
return v.boolean; return v.boolean;
} }
@ -936,7 +949,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v) inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
{ {
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type != tAttrs) if (v.type() != nAttrs)
throwTypeError("value is %1% while a set was expected", v); throwTypeError("value is %1% while a set was expected", v);
} }
@ -1036,7 +1049,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Value nameVal; Value nameVal;
i.nameExpr->eval(state, *dynamicEnv, nameVal); i.nameExpr->eval(state, *dynamicEnv, nameVal);
state.forceValue(nameVal, i.pos); state.forceValue(nameVal, i.pos);
if (nameVal.type == tNull) if (nameVal.type() == nNull)
continue; continue;
state.forceStringNoCtx(nameVal); state.forceStringNoCtx(nameVal);
Symbol nameSym = state.symbols.create(nameVal.string.s); Symbol nameSym = state.symbols.create(nameVal.string.s);
@ -1121,7 +1134,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
Symbol name = getName(i, state, env); Symbol name = getName(i, state, env);
if (def) { if (def) {
state.forceValue(*vAttrs, pos); state.forceValue(*vAttrs, pos);
if (vAttrs->type != tAttrs || if (vAttrs->type() != nAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{ {
def->eval(state, env, v); def->eval(state, env, v);
@ -1161,7 +1174,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
state.forceValue(*vAttrs); state.forceValue(*vAttrs);
Bindings::iterator j; Bindings::iterator j;
Symbol name = getName(i, state, env); Symbol name = getName(i, state, env);
if (vAttrs->type != tAttrs || if (vAttrs->type() != nAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{ {
mkBool(v, false); mkBool(v, false);
@ -1177,9 +1190,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
void ExprLambda::eval(EvalState & state, Env & env, Value & v) void ExprLambda::eval(EvalState & state, Env & env, Value & v)
{ {
v.type = tLambda; v.mkLambda(&env, this);
v.lambda.env = &env;
v.lambda.fun = this;
} }
@ -1197,11 +1208,11 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
/* Figure out the number of arguments still needed. */ /* Figure out the number of arguments still needed. */
size_t argsDone = 0; size_t argsDone = 0;
Value * primOp = &fun; Value * primOp = &fun;
while (primOp->type == tPrimOpApp) { while (primOp->isPrimOpApp()) {
argsDone++; argsDone++;
primOp = primOp->primOpApp.left; primOp = primOp->primOpApp.left;
} }
assert(primOp->type == tPrimOp); assert(primOp->isPrimOp());
auto arity = primOp->primOp->arity; auto arity = primOp->primOp->arity;
auto argsLeft = arity - argsDone; auto argsLeft = arity - argsDone;
@ -1212,7 +1223,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
Value * vArgs[arity]; Value * vArgs[arity];
auto n = arity - 1; auto n = arity - 1;
vArgs[n--] = &arg; vArgs[n--] = &arg;
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left) for (Value * arg = &fun; arg->isPrimOpApp(); arg = arg->primOpApp.left)
vArgs[n--] = arg->primOpApp.right; vArgs[n--] = arg->primOpApp.right;
/* And call the primop. */ /* And call the primop. */
@ -1222,9 +1233,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
} else { } else {
Value * fun2 = allocValue(); Value * fun2 = allocValue();
*fun2 = fun; *fun2 = fun;
v.type = tPrimOpApp; v.mkPrimOpApp(fun2, &arg);
v.primOpApp.left = fun2;
v.primOpApp.right = &arg;
} }
} }
@ -1234,12 +1243,12 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
forceValue(fun, pos); forceValue(fun, pos);
if (fun.type == tPrimOp || fun.type == tPrimOpApp) { if (fun.isPrimOp() || fun.isPrimOpApp()) {
callPrimOp(fun, arg, v, pos); callPrimOp(fun, arg, v, pos);
return; return;
} }
if (fun.type == tAttrs) { if (fun.type() == nAttrs) {
auto found = fun.attrs->find(sFunctor); auto found = fun.attrs->find(sFunctor);
if (found != fun.attrs->end()) { if (found != fun.attrs->end()) {
/* fun may be allocated on the stack of the calling function, /* fun may be allocated on the stack of the calling function,
@ -1255,7 +1264,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
} }
} }
if (fun.type != tLambda) if (!fun.isLambda())
throwTypeError(pos, "attempt to call something which is not a function but %1%", fun); throwTypeError(pos, "attempt to call something which is not a function but %1%", fun);
ExprLambda & lambda(*fun.lambda.fun); ExprLambda & lambda(*fun.lambda.fun);
@ -1338,7 +1347,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
{ {
forceValue(fun); forceValue(fun);
if (fun.type == tAttrs) { if (fun.type() == nAttrs) {
auto found = fun.attrs->find(sFunctor); auto found = fun.attrs->find(sFunctor);
if (found != fun.attrs->end()) { if (found != fun.attrs->end()) {
Value * v = allocValue(); Value * v = allocValue();
@ -1348,7 +1357,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
} }
} }
if (fun.type != tLambda || !fun.lambda.fun->matchAttrs) { if (!fun.isLambda() || !fun.lambda.fun->matchAttrs) {
res = fun; res = fun;
return; return;
} }
@ -1370,7 +1379,13 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
if (j != args.end()) { if (j != args.end()) {
actualArgs->attrs->push_back(*j); actualArgs->attrs->push_back(*j);
} else if (!i.def) { } else if (!i.def) {
throwTypeError("cannot auto-call a function that has an argument without a default value ('%1%')", i.name); throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See
https://nixos.org/manual/nix/stable/#ss-functions.)", i.name);
} }
} }
} }
@ -1404,7 +1419,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
if (!state.evalBool(env, cond, pos)) { if (!state.evalBool(env, cond, pos)) {
std::ostringstream out; std::ostringstream out;
cond->show(out); cond->show(out);
throwAssertionError(pos, "assertion '%1%' failed at %2%", out.str()); throwAssertionError(pos, "assertion '%1%' failed", out.str());
} }
body->eval(state, env, v); body->eval(state, env, v);
} }
@ -1532,7 +1547,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
NixFloat nf = 0; NixFloat nf = 0;
bool first = !forceString; bool first = !forceString;
ValueType firstType = tString; ValueType firstType = nString;
for (auto & i : *es) { for (auto & i : *es) {
Value vTmp; Value vTmp;
@ -1543,36 +1558,36 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
since paths are copied when they are used in a derivation), since paths are copied when they are used in a derivation),
and none of the strings are allowed to have contexts. */ and none of the strings are allowed to have contexts. */
if (first) { if (first) {
firstType = vTmp.type; firstType = vTmp.type();
first = false; first = false;
} }
if (firstType == tInt) { if (firstType == nInt) {
if (vTmp.type == tInt) { if (vTmp.type() == nInt) {
n += vTmp.integer; n += vTmp.integer;
} else if (vTmp.type == tFloat) { } else if (vTmp.type() == nFloat) {
// Upgrade the type from int to float; // Upgrade the type from int to float;
firstType = tFloat; firstType = nFloat;
nf = n; nf = n;
nf += vTmp.fpoint; nf += vTmp.fpoint;
} else } else
throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp)); throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp));
} else if (firstType == tFloat) { } else if (firstType == nFloat) {
if (vTmp.type == tInt) { if (vTmp.type() == nInt) {
nf += vTmp.integer; nf += vTmp.integer;
} else if (vTmp.type == tFloat) { } else if (vTmp.type() == nFloat) {
nf += vTmp.fpoint; nf += vTmp.fpoint;
} else } else
throwEvalError(pos, "cannot add %1% to a float", showType(vTmp)); throwEvalError(pos, "cannot add %1% to a float", showType(vTmp));
} else } else
s << state.coerceToString(pos, vTmp, context, false, firstType == tString); s << state.coerceToString(pos, vTmp, context, false, firstType == nString);
} }
if (firstType == tInt) if (firstType == nInt)
mkInt(v, n); mkInt(v, n);
else if (firstType == tFloat) else if (firstType == nFloat)
mkFloat(v, nf); mkFloat(v, nf);
else if (firstType == tPath) { else if (firstType == nPath) {
if (!context.empty()) if (!context.empty())
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path"); throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
auto path = canonPath(s.str()); auto path = canonPath(s.str());
@ -1599,7 +1614,7 @@ void EvalState::forceValueDeep(Value & v)
forceValue(v); forceValue(v);
if (v.type == tAttrs) { if (v.type() == nAttrs) {
for (auto & i : *v.attrs) for (auto & i : *v.attrs)
try { try {
recurse(*i.value); recurse(*i.value);
@ -1622,7 +1637,7 @@ void EvalState::forceValueDeep(Value & v)
NixInt EvalState::forceInt(Value & v, const Pos & pos) NixInt EvalState::forceInt(Value & v, const Pos & pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type != tInt) if (v.type() != nInt)
throwTypeError(pos, "value is %1% while an integer was expected", v); throwTypeError(pos, "value is %1% while an integer was expected", v);
return v.integer; return v.integer;
} }
@ -1631,9 +1646,9 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
NixFloat EvalState::forceFloat(Value & v, const Pos & pos) NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type == tInt) if (v.type() == nInt)
return v.integer; return v.integer;
else if (v.type != tFloat) else if (v.type() != nFloat)
throwTypeError(pos, "value is %1% while a float was expected", v); throwTypeError(pos, "value is %1% while a float was expected", v);
return v.fpoint; return v.fpoint;
} }
@ -1642,7 +1657,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
bool EvalState::forceBool(Value & v, const Pos & pos) bool EvalState::forceBool(Value & v, const Pos & pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type != tBool) if (v.type() != nBool)
throwTypeError(pos, "value is %1% while a Boolean was expected", v); throwTypeError(pos, "value is %1% while a Boolean was expected", v);
return v.boolean; return v.boolean;
} }
@ -1650,14 +1665,14 @@ bool EvalState::forceBool(Value & v, const Pos & pos)
bool EvalState::isFunctor(Value & fun) bool EvalState::isFunctor(Value & fun)
{ {
return fun.type == tAttrs && fun.attrs->find(sFunctor) != fun.attrs->end(); return fun.type() == nAttrs && fun.attrs->find(sFunctor) != fun.attrs->end();
} }
void EvalState::forceFunction(Value & v, const Pos & pos) void EvalState::forceFunction(Value & v, const Pos & pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp && !isFunctor(v)) if (v.type() != nFunction && !isFunctor(v))
throwTypeError(pos, "value is %1% while a function was expected", v); throwTypeError(pos, "value is %1% while a function was expected", v);
} }
@ -1665,7 +1680,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
string EvalState::forceString(Value & v, const Pos & pos) string EvalState::forceString(Value & v, const Pos & pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type != tString) { if (v.type() != nString) {
if (pos) if (pos)
throwTypeError(pos, "value is %1% while a string was expected", v); throwTypeError(pos, "value is %1% while a string was expected", v);
else else
@ -1698,7 +1713,7 @@ void copyContext(const Value & v, PathSet & context)
std::vector<std::pair<Path, std::string>> Value::getContext() std::vector<std::pair<Path, std::string>> Value::getContext()
{ {
std::vector<std::pair<Path, std::string>> res; std::vector<std::pair<Path, std::string>> res;
assert(type == tString); assert(internalType == tString);
if (string.context) if (string.context)
for (const char * * p = string.context; *p; ++p) for (const char * * p = string.context; *p; ++p)
res.push_back(decodeContext(*p)); res.push_back(decodeContext(*p));
@ -1731,11 +1746,11 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
bool EvalState::isDerivation(Value & v) bool EvalState::isDerivation(Value & v)
{ {
if (v.type != tAttrs) return false; if (v.type() != nAttrs) return false;
Bindings::iterator i = v.attrs->find(sType); Bindings::iterator i = v.attrs->find(sType);
if (i == v.attrs->end()) return false; if (i == v.attrs->end()) return false;
forceValue(*i->value); forceValue(*i->value);
if (i->value->type != tString) return false; if (i->value->type() != nString) return false;
return strcmp(i->value->string.s, "derivation") == 0; return strcmp(i->value->string.s, "derivation") == 0;
} }
@ -1760,17 +1775,17 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
string s; string s;
if (v.type == tString) { if (v.type() == nString) {
copyContext(v, context); copyContext(v, context);
return v.string.s; return v.string.s;
} }
if (v.type == tPath) { if (v.type() == nPath) {
Path path(canonPath(v.path)); Path path(canonPath(v.path));
return copyToStore ? copyPathToStore(context, path) : path; return copyToStore ? copyPathToStore(context, path) : path;
} }
if (v.type == tAttrs) { if (v.type() == nAttrs) {
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore); auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
if (maybeString) { if (maybeString) {
return *maybeString; return *maybeString;
@ -1780,18 +1795,18 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
return coerceToString(pos, *i->value, context, coerceMore, copyToStore); return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
} }
if (v.type == tExternal) if (v.type() == nExternal)
return v.external->coerceToString(pos, context, coerceMore, copyToStore); return v.external->coerceToString(pos, context, coerceMore, copyToStore);
if (coerceMore) { if (coerceMore) {
/* Note that `false' is represented as an empty string for /* Note that `false' is represented as an empty string for
shell scripting convenience, just like `null'. */ shell scripting convenience, just like `null'. */
if (v.type == tBool && v.boolean) return "1"; if (v.type() == nBool && v.boolean) return "1";
if (v.type == tBool && !v.boolean) return ""; if (v.type() == nBool && !v.boolean) return "";
if (v.type == tInt) return std::to_string(v.integer); if (v.type() == nInt) return std::to_string(v.integer);
if (v.type == tFloat) return std::to_string(v.fpoint); if (v.type() == nFloat) return std::to_string(v.fpoint);
if (v.type == tNull) return ""; if (v.type() == nNull) return "";
if (v.isList()) { if (v.isList()) {
string result; string result;
@ -1854,40 +1869,38 @@ bool EvalState::eqValues(Value & v1, Value & v2)
if (&v1 == &v2) return true; if (&v1 == &v2) return true;
// Special case type-compatibility between float and int // Special case type-compatibility between float and int
if (v1.type == tInt && v2.type == tFloat) if (v1.type() == nInt && v2.type() == nFloat)
return v1.integer == v2.fpoint; return v1.integer == v2.fpoint;
if (v1.type == tFloat && v2.type == tInt) if (v1.type() == nFloat && v2.type() == nInt)
return v1.fpoint == v2.integer; return v1.fpoint == v2.integer;
// All other types are not compatible with each other. // All other types are not compatible with each other.
if (v1.type != v2.type) return false; if (v1.type() != v2.type()) return false;
switch (v1.type) { switch (v1.type()) {
case tInt: case nInt:
return v1.integer == v2.integer; return v1.integer == v2.integer;
case tBool: case nBool:
return v1.boolean == v2.boolean; return v1.boolean == v2.boolean;
case tString: case nString:
return strcmp(v1.string.s, v2.string.s) == 0; return strcmp(v1.string.s, v2.string.s) == 0;
case tPath: case nPath:
return strcmp(v1.path, v2.path) == 0; return strcmp(v1.path, v2.path) == 0;
case tNull: case nNull:
return true; return true;
case tList1: case nList:
case tList2:
case tListN:
if (v1.listSize() != v2.listSize()) return false; if (v1.listSize() != v2.listSize()) return false;
for (size_t n = 0; n < v1.listSize(); ++n) for (size_t n = 0; n < v1.listSize(); ++n)
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false; if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
return true; return true;
case tAttrs: { case nAttrs: {
/* If both sets denote a derivation (type = "derivation"), /* If both sets denote a derivation (type = "derivation"),
then compare their outPaths. */ then compare their outPaths. */
if (isDerivation(v1) && isDerivation(v2)) { if (isDerivation(v1) && isDerivation(v2)) {
@ -1909,15 +1922,13 @@ bool EvalState::eqValues(Value & v1, Value & v2)
} }
/* Functions are incomparable. */ /* Functions are incomparable. */
case tLambda: case nFunction:
case tPrimOp:
case tPrimOpApp:
return false; return false;
case tExternal: case nExternal:
return *v1.external == *v2.external; return *v1.external == *v2.external;
case tFloat: case nFloat:
return v1.fpoint == v2.fpoint; return v1.fpoint == v2.fpoint;
default: default:
@ -2046,7 +2057,7 @@ void EvalState::printStats()
string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
{ {
throw TypeError({ throw TypeError({
.hint = hintfmt("cannot coerce %1% to a string", showType()), .msg = hintfmt("cannot coerce %1% to a string", showType()),
.errPos = pos .errPos = pos
}); });
} }
@ -2072,10 +2083,19 @@ EvalSettings::EvalSettings()
Strings EvalSettings::getDefaultNixPath() Strings EvalSettings::getDefaultNixPath()
{ {
Strings res; Strings res;
auto add = [&](const Path & p) { if (pathExists(p)) { res.push_back(p); } }; auto add = [&](const Path & p, const std::string & s = std::string()) {
if (pathExists(p)) {
if (s.empty()) {
res.push_back(p);
} else {
res.push_back(s + "=" + p);
}
}
};
add(getHome() + "/.nix-defexpr/channels"); add(getHome() + "/.nix-defexpr/channels");
add("nixpkgs=" + settings.nixStateDir + "/nix/profiles/per-user/root/channels/nixpkgs"); add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
add(settings.nixStateDir + "/nix/profiles/per-user/root/channels"); add(settings.nixStateDir + "/profiles/per-user/root/channels");
return res; return res;
} }

View file

@ -432,4 +432,6 @@ struct EvalSettings : Config
extern EvalSettings evalSettings; extern EvalSettings evalSettings;
static const std::string corepkgsPrefix{"/__corepkgs__/"};
} }

View file

@ -1,6 +1,6 @@
{ system ? "" # obsolete { system ? "" # obsolete
, url , url
, hash ? "" # an SRI ash , hash ? "" # an SRI hash
# Legacy hash specification # Legacy hash specification
, md5 ? "", sha1 ? "", sha256 ? "", sha512 ? "" , md5 ? "", sha1 ? "", sha256 ? "", sha512 ? ""

View file

@ -0,0 +1,81 @@
#include "flake.hh"
#include <nlohmann/json.hpp>
namespace nix::flake {
// setting name -> setting value -> allow or ignore.
typedef std::map<std::string, std::map<std::string, bool>> TrustedList;
Path trustedListPath()
{
return getDataDir() + "/nix/trusted-settings.json";
}
static TrustedList readTrustedList()
{
auto path = trustedListPath();
if (!pathExists(path)) return {};
auto json = nlohmann::json::parse(readFile(path));
return json;
}
static void writeTrustedList(const TrustedList & trustedList)
{
writeFile(trustedListPath(), nlohmann::json(trustedList).dump());
}
void ConfigFile::apply()
{
std::set<std::string> whitelist{"bash-prompt", "bash-prompt-suffix"};
for (auto & [name, value] : settings) {
auto baseName = hasPrefix(name, "extra-") ? std::string(name, 6) : name;
// FIXME: Move into libutil/config.cc.
std::string valueS;
if (auto s = std::get_if<std::string>(&value))
valueS = *s;
else if (auto n = std::get_if<int64_t>(&value))
valueS = fmt("%d", n);
else if (auto b = std::get_if<Explicit<bool>>(&value))
valueS = b->t ? "true" : "false";
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
valueS = concatStringsSep(" ", *ss); // FIXME: evil
else
assert(false);
if (!whitelist.count(baseName)) {
auto trustedList = readTrustedList();
bool trusted = false;
if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) {
trusted = *saved;
} else {
// FIXME: filter ANSI escapes, newlines, \r, etc.
if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) != 'y') {
if (std::tolower(logger->ask("do you want to permanently mark this value as untrusted (y/N)?").value_or('n')) == 'y') {
trustedList[name][valueS] = false;
writeTrustedList(trustedList);
}
} else {
if (std::tolower(logger->ask("do you want to permanently mark this value as trusted (y/N)?").value_or('n')) == 'y') {
trustedList[name][valueS] = trusted = true;
writeTrustedList(trustedList);
}
}
}
if (!trusted) {
warn("ignoring untrusted flake configuration setting '%s'", name);
continue;
}
}
globalConfig.set(name, valueS);
}
}
}

View file

@ -71,14 +71,20 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
return {std::move(tree), resolvedRef, lockedRef}; return {std::move(tree), resolvedRef, lockedRef};
} }
static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
{
if (value.isThunk() && value.isTrivial())
state.forceValue(value, pos);
}
static void expectType(EvalState & state, ValueType type, static void expectType(EvalState & state, ValueType type,
Value & value, const Pos & pos) Value & value, const Pos & pos)
{ {
if (value.type == tThunk && value.isTrivial()) forceTrivialValue(state, value, pos);
state.forceValue(value, pos); if (value.type() != type)
if (value.type != type)
throw Error("expected %s but got %s at %s", throw Error("expected %s but got %s at %s",
showType(type), showType(value.type), pos); showType(type), showType(value.type()), pos);
} }
static std::map<FlakeId, FlakeInput> parseFlakeInputs( static std::map<FlakeId, FlakeInput> parseFlakeInputs(
@ -87,7 +93,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
static FlakeInput parseFlakeInput(EvalState & state, static FlakeInput parseFlakeInput(EvalState & state,
const std::string & inputName, Value * value, const Pos & pos) const std::string & inputName, Value * value, const Pos & pos)
{ {
expectType(state, tAttrs, *value, pos); expectType(state, nAttrs, *value, pos);
FlakeInput input; FlakeInput input;
@ -102,24 +108,32 @@ static FlakeInput parseFlakeInput(EvalState & state,
for (nix::Attr attr : *(value->attrs)) { for (nix::Attr attr : *(value->attrs)) {
try { try {
if (attr.name == sUrl) { if (attr.name == sUrl) {
expectType(state, tString, *attr.value, *attr.pos); expectType(state, nString, *attr.value, *attr.pos);
url = attr.value->string.s; url = attr.value->string.s;
attrs.emplace("url", *url); attrs.emplace("url", *url);
} else if (attr.name == sFlake) { } else if (attr.name == sFlake) {
expectType(state, tBool, *attr.value, *attr.pos); expectType(state, nBool, *attr.value, *attr.pos);
input.isFlake = attr.value->boolean; input.isFlake = attr.value->boolean;
} else if (attr.name == sInputs) { } else if (attr.name == sInputs) {
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos); input.overrides = parseFlakeInputs(state, attr.value, *attr.pos);
} else if (attr.name == sFollows) { } else if (attr.name == sFollows) {
expectType(state, tString, *attr.value, *attr.pos); expectType(state, nString, *attr.value, *attr.pos);
input.follows = parseInputPath(attr.value->string.s); input.follows = parseInputPath(attr.value->string.s);
} else { } else {
state.forceValue(*attr.value); switch (attr.value->type()) {
if (attr.value->type == tString) case nString:
attrs.emplace(attr.name, attr.value->string.s); attrs.emplace(attr.name, attr.value->string.s);
else break;
throw TypeError("flake input attribute '%s' is %s while a string is expected", case nBool:
attr.name, showType(*attr.value)); attrs.emplace(attr.name, Explicit<bool> { attr.value->boolean });
break;
case nInt:
attrs.emplace(attr.name, (long unsigned int)attr.value->integer);
break;
default:
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
attr.name, showType(*attr.value));
}
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name)); e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name));
@ -153,7 +167,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
{ {
std::map<FlakeId, FlakeInput> inputs; std::map<FlakeId, FlakeInput> inputs;
expectType(state, tAttrs, *value, pos); expectType(state, nAttrs, *value, pos);
for (nix::Attr & inputAttr : *(*value).attrs) { for (nix::Attr & inputAttr : *(*value).attrs) {
inputs.emplace(inputAttr.name, inputs.emplace(inputAttr.name,
@ -194,15 +208,10 @@ static Flake getFlake(
Value vInfo; Value vInfo;
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
expectType(state, tAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0)); expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
auto sEdition = state.symbols.create("edition"); // FIXME: remove soon
if (vInfo.attrs->get(sEdition))
warn("flake '%s' has deprecated attribute 'edition'", lockedRef);
if (auto description = vInfo.attrs->get(state.sDescription)) { if (auto description = vInfo.attrs->get(state.sDescription)) {
expectType(state, tString, *description->value, *description->pos); expectType(state, nString, *description->value, *description->pos);
flake.description = description->value->string.s; flake.description = description->value->string.s;
} }
@ -214,9 +223,9 @@ static Flake getFlake(
auto sOutputs = state.symbols.create("outputs"); auto sOutputs = state.symbols.create("outputs");
if (auto outputs = vInfo.attrs->get(sOutputs)) { if (auto outputs = vInfo.attrs->get(sOutputs)) {
expectType(state, tLambda, *outputs->value, *outputs->pos); expectType(state, nFunction, *outputs->value, *outputs->pos);
if (outputs->value->lambda.fun->matchAttrs) { if (outputs->value->isLambda() && outputs->value->lambda.fun->matchAttrs) {
for (auto & formal : outputs->value->lambda.fun->formals->formals) { for (auto & formal : outputs->value->lambda.fun->formals->formals) {
if (formal.name != state.sSelf) if (formal.name != state.sSelf)
flake.inputs.emplace(formal.name, FlakeInput { flake.inputs.emplace(formal.name, FlakeInput {
@ -228,11 +237,41 @@ static Flake getFlake(
} else } else
throw Error("flake '%s' lacks attribute 'outputs'", lockedRef); throw Error("flake '%s' lacks attribute 'outputs'", lockedRef);
auto sNixConfig = state.symbols.create("nixConfig");
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
expectType(state, nAttrs, *nixConfig->value, *nixConfig->pos);
for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos);
if (setting.value->type() == nString)
flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)});
else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
else if (setting.value->type() == nBool)
flake.config.settings.insert({setting.name, state.forceBool(*setting.value, *setting.pos)});
else if (setting.value->type() == nList) {
std::vector<std::string> ss;
for (unsigned int n = 0; n < setting.value->listSize(); ++n) {
auto elem = setting.value->listElems()[n];
if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value));
ss.push_back(state.forceStringNoCtx(*elem, *setting.pos));
}
flake.config.settings.insert({setting.name, ss});
}
else
throw TypeError("flake configuration setting '%s' is %s",
setting.name, showType(*setting.value));
}
}
for (auto & attr : *vInfo.attrs) { for (auto & attr : *vInfo.attrs) {
if (attr.name != sEdition && if (attr.name != state.sDescription &&
attr.name != state.sDescription &&
attr.name != sInputs && attr.name != sInputs &&
attr.name != sOutputs) attr.name != sOutputs &&
attr.name != sNixConfig)
throw Error("flake '%s' has an unsupported attribute '%s', at %s", throw Error("flake '%s' has an unsupported attribute '%s', at %s",
lockedRef, attr.name, *attr.pos); lockedRef, attr.name, *attr.pos);
} }
@ -259,284 +298,298 @@ LockedFlake lockFlake(
auto flake = getFlake(state, topRef, lockFlags.useRegistries, flakeCache); auto flake = getFlake(state, topRef, lockFlags.useRegistries, flakeCache);
// FIXME: symlink attack try {
auto oldLockFile = LockFile::read(
flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock");
debug("old lock file: %s", oldLockFile); // FIXME: symlink attack
auto oldLockFile = LockFile::read(
flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock");
// FIXME: check whether all overrides are used. debug("old lock file: %s", oldLockFile);
std::map<InputPath, FlakeInput> overrides;
std::set<InputPath> overridesUsed, updatesUsed;
for (auto & i : lockFlags.inputOverrides) // FIXME: check whether all overrides are used.
overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second }); std::map<InputPath, FlakeInput> overrides;
std::set<InputPath> overridesUsed, updatesUsed;
LockFile newLockFile; for (auto & i : lockFlags.inputOverrides)
overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second });
std::vector<FlakeRef> parents; LockFile newLockFile;
std::function<void( std::vector<FlakeRef> parents;
const FlakeInputs & flakeInputs,
std::shared_ptr<Node> node,
const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode)>
computeLocks;
computeLocks = [&]( std::function<void(
const FlakeInputs & flakeInputs, const FlakeInputs & flakeInputs,
std::shared_ptr<Node> node, std::shared_ptr<Node> node,
const InputPath & inputPathPrefix, const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode) std::shared_ptr<const Node> oldNode)>
{ computeLocks;
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
/* Get the overrides (i.e. attributes of the form computeLocks = [&](
'inputs.nixops.inputs.nixpkgs.url = ...'). */ const FlakeInputs & flakeInputs,
// FIXME: check this std::shared_ptr<Node> node,
for (auto & [id, input] : flake.inputs) { const InputPath & inputPathPrefix,
for (auto & [idOverride, inputOverride] : input.overrides) { std::shared_ptr<const Node> oldNode)
{
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
/* Get the overrides (i.e. attributes of the form
'inputs.nixops.inputs.nixpkgs.url = ...'). */
// FIXME: check this
for (auto & [id, input] : flake.inputs) {
for (auto & [idOverride, inputOverride] : input.overrides) {
auto inputPath(inputPathPrefix);
inputPath.push_back(id);
inputPath.push_back(idOverride);
overrides.insert_or_assign(inputPath, inputOverride);
}
}
/* Go over the flake inputs, resolve/fetch them if
necessary (i.e. if they're new or the flakeref changed
from what's in the lock file). */
for (auto & [id, input2] : flakeInputs) {
auto inputPath(inputPathPrefix); auto inputPath(inputPathPrefix);
inputPath.push_back(id); inputPath.push_back(id);
inputPath.push_back(idOverride); auto inputPathS = printInputPath(inputPath);
overrides.insert_or_assign(inputPath, inputOverride); debug("computing input '%s'", inputPathS);
}
}
/* Go over the flake inputs, resolve/fetch them if try {
necessary (i.e. if they're new or the flakeref changed
from what's in the lock file). */
for (auto & [id, input2] : flakeInputs) {
auto inputPath(inputPathPrefix);
inputPath.push_back(id);
auto inputPathS = printInputPath(inputPath);
debug("computing input '%s'", inputPathS);
/* Do we have an override for this input from one of the /* Do we have an override for this input from one of the
ancestors? */ ancestors? */
auto i = overrides.find(inputPath); auto i = overrides.find(inputPath);
bool hasOverride = i != overrides.end(); bool hasOverride = i != overrides.end();
if (hasOverride) overridesUsed.insert(inputPath); if (hasOverride) overridesUsed.insert(inputPath);
auto & input = hasOverride ? i->second : input2; auto & input = hasOverride ? i->second : input2;
/* Resolve 'follows' later (since it may refer to an input /* Resolve 'follows' later (since it may refer to an input
path we haven't processed yet. */ path we haven't processed yet. */
if (input.follows) { if (input.follows) {
InputPath target; InputPath target;
if (hasOverride || input.absolute) if (hasOverride || input.absolute)
/* 'follows' from an override is relative to the /* 'follows' from an override is relative to the
root of the graph. */ root of the graph. */
target = *input.follows; target = *input.follows;
else { else {
/* Otherwise, it's relative to the current flake. */ /* Otherwise, it's relative to the current flake. */
target = inputPathPrefix; target = inputPathPrefix;
for (auto & i : *input.follows) target.push_back(i); for (auto & i : *input.follows) target.push_back(i);
} }
debug("input '%s' follows '%s'", inputPathS, printInputPath(target)); debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
node->inputs.insert_or_assign(id, target); node->inputs.insert_or_assign(id, target);
continue; continue;
} }
assert(input.ref); assert(input.ref);
/* Do we have an entry in the existing lock file? And we /* Do we have an entry in the existing lock file? And we
don't have a --update-input flag for this input? */ don't have a --update-input flag for this input? */
std::shared_ptr<LockedNode> oldLock; std::shared_ptr<LockedNode> oldLock;
updatesUsed.insert(inputPath); updatesUsed.insert(inputPath);
if (oldNode && !lockFlags.inputUpdates.count(inputPath)) if (oldNode && !lockFlags.inputUpdates.count(inputPath))
if (auto oldLock2 = get(oldNode->inputs, id)) if (auto oldLock2 = get(oldNode->inputs, id))
if (auto oldLock3 = std::get_if<0>(&*oldLock2)) if (auto oldLock3 = std::get_if<0>(&*oldLock2))
oldLock = *oldLock3; oldLock = *oldLock3;
if (oldLock if (oldLock
&& oldLock->originalRef == *input.ref && oldLock->originalRef == *input.ref
&& !hasOverride) && !hasOverride)
{ {
debug("keeping existing input '%s'", inputPathS); debug("keeping existing input '%s'", inputPathS);
/* Copy the input from the old lock since its flakeref /* Copy the input from the old lock since its flakeref
didn't change and there is no override from a didn't change and there is no override from a
higher level flake. */ higher level flake. */
auto childNode = std::make_shared<LockedNode>( auto childNode = std::make_shared<LockedNode>(
oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake); oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake);
node->inputs.insert_or_assign(id, childNode); node->inputs.insert_or_assign(id, childNode);
/* If we have an --update-input flag for an input /* If we have an --update-input flag for an input
of this input, then we must fetch the flake to of this input, then we must fetch the flake to
update it. */ update it. */
auto lb = lockFlags.inputUpdates.lower_bound(inputPath); auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
auto hasChildUpdate = auto hasChildUpdate =
lb != lockFlags.inputUpdates.end() lb != lockFlags.inputUpdates.end()
&& lb->size() > inputPath.size() && lb->size() > inputPath.size()
&& std::equal(inputPath.begin(), inputPath.end(), lb->begin()); && std::equal(inputPath.begin(), inputPath.end(), lb->begin());
if (hasChildUpdate) { if (hasChildUpdate) {
auto inputFlake = getFlake( auto inputFlake = getFlake(
state, oldLock->lockedRef, false, flakeCache); state, oldLock->lockedRef, false, flakeCache);
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock); computeLocks(inputFlake.inputs, childNode, inputPath, oldLock);
} else { } else {
/* No need to fetch this flake, we can be /* No need to fetch this flake, we can be
lazy. However there may be new overrides on the lazy. However there may be new overrides on the
inputs of this flake, so we need to check inputs of this flake, so we need to check
those. */ those. */
FlakeInputs fakeInputs; FlakeInputs fakeInputs;
for (auto & i : oldLock->inputs) { for (auto & i : oldLock->inputs) {
if (auto lockedNode = std::get_if<0>(&i.second)) { if (auto lockedNode = std::get_if<0>(&i.second)) {
fakeInputs.emplace(i.first, FlakeInput { fakeInputs.emplace(i.first, FlakeInput {
.ref = (*lockedNode)->originalRef, .ref = (*lockedNode)->originalRef,
.isFlake = (*lockedNode)->isFlake, .isFlake = (*lockedNode)->isFlake,
}); });
} else if (auto follows = std::get_if<1>(&i.second)) { } else if (auto follows = std::get_if<1>(&i.second)) {
fakeInputs.emplace(i.first, FlakeInput { fakeInputs.emplace(i.first, FlakeInput {
.follows = *follows, .follows = *follows,
.absolute = true .absolute = true
}); });
}
}
computeLocks(fakeInputs, childNode, inputPath, oldLock);
}
} else {
/* We need to create a new lock file entry. So fetch
this input. */
debug("creating new input '%s'", inputPathS);
if (!lockFlags.allowMutable && !input.ref->input.isImmutable())
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
if (input.isFlake) {
auto inputFlake = getFlake(state, *input.ref, lockFlags.useRegistries, flakeCache);
/* Note: in case of an --override-input, we use
the *original* ref (input2.ref) for the
"original" field, rather than the
override. This ensures that the override isn't
nuked the next time we update the lock
file. That is, overrides are sticky unless you
use --no-write-lock-file. */
auto childNode = std::make_shared<LockedNode>(
inputFlake.lockedRef, input2.ref ? *input2.ref : *input.ref);
node->inputs.insert_or_assign(id, childNode);
/* Guard against circular flake imports. */
for (auto & parent : parents)
if (parent == *input.ref)
throw Error("found circular import of flake '%s'", parent);
parents.push_back(*input.ref);
Finally cleanup([&]() { parents.pop_back(); });
/* Recursively process the inputs of this
flake. Also, unless we already have this flake
in the top-level lock file, use this flake's
own lock file. */
computeLocks(
inputFlake.inputs, childNode, inputPath,
oldLock
? std::dynamic_pointer_cast<const Node>(oldLock)
: LockFile::read(
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root);
}
else {
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, *input.ref, lockFlags.useRegistries, flakeCache);
node->inputs.insert_or_assign(id,
std::make_shared<LockedNode>(lockedRef, *input.ref, false));
} }
} }
computeLocks(fakeInputs, childNode, inputPath, oldLock); } catch (Error & e) {
} e.addTrace({}, "while updating the flake input '%s'", inputPathS);
throw;
} else {
/* We need to create a new lock file entry. So fetch
this input. */
debug("creating new input '%s'", inputPathS);
if (!lockFlags.allowMutable && !input.ref->input.isImmutable())
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
if (input.isFlake) {
auto inputFlake = getFlake(state, *input.ref, lockFlags.useRegistries, flakeCache);
/* Note: in case of an --override-input, we use
the *original* ref (input2.ref) for the
"original" field, rather than the
override. This ensures that the override isn't
nuked the next time we update the lock
file. That is, overrides are sticky unless you
use --no-write-lock-file. */
auto childNode = std::make_shared<LockedNode>(
inputFlake.lockedRef, input2.ref ? *input2.ref : *input.ref);
node->inputs.insert_or_assign(id, childNode);
/* Guard against circular flake imports. */
for (auto & parent : parents)
if (parent == *input.ref)
throw Error("found circular import of flake '%s'", parent);
parents.push_back(*input.ref);
Finally cleanup([&]() { parents.pop_back(); });
/* Recursively process the inputs of this
flake. Also, unless we already have this flake
in the top-level lock file, use this flake's
own lock file. */
computeLocks(
inputFlake.inputs, childNode, inputPath,
oldLock
? std::dynamic_pointer_cast<const Node>(oldLock)
: LockFile::read(
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root);
}
else {
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, *input.ref, lockFlags.useRegistries, flakeCache);
node->inputs.insert_or_assign(id,
std::make_shared<LockedNode>(lockedRef, *input.ref, false));
} }
} }
} };
};
computeLocks( computeLocks(
flake.inputs, newLockFile.root, {}, flake.inputs, newLockFile.root, {},
lockFlags.recreateLockFile ? nullptr : oldLockFile.root); lockFlags.recreateLockFile ? nullptr : oldLockFile.root);
for (auto & i : lockFlags.inputOverrides) for (auto & i : lockFlags.inputOverrides)
if (!overridesUsed.count(i.first)) if (!overridesUsed.count(i.first))
warn("the flag '--override-input %s %s' does not match any input", warn("the flag '--override-input %s %s' does not match any input",
printInputPath(i.first), i.second); printInputPath(i.first), i.second);
for (auto & i : lockFlags.inputUpdates) for (auto & i : lockFlags.inputUpdates)
if (!updatesUsed.count(i)) if (!updatesUsed.count(i))
warn("the flag '--update-input %s' does not match any input", printInputPath(i)); warn("the flag '--update-input %s' does not match any input", printInputPath(i));
/* Check 'follows' inputs. */ /* Check 'follows' inputs. */
newLockFile.check(); newLockFile.check();
debug("new lock file: %s", newLockFile); debug("new lock file: %s", newLockFile);
/* Check whether we need to / can write the new lock file. */ /* Check whether we need to / can write the new lock file. */
if (!(newLockFile == oldLockFile)) { if (!(newLockFile == oldLockFile)) {
auto diff = LockFile::diff(oldLockFile, newLockFile); auto diff = LockFile::diff(oldLockFile, newLockFile);
if (lockFlags.writeLockFile) { if (lockFlags.writeLockFile) {
if (auto sourcePath = topRef.input.getSourcePath()) { if (auto sourcePath = topRef.input.getSourcePath()) {
if (!newLockFile.isImmutable()) { if (!newLockFile.isImmutable()) {
if (settings.warnDirty) if (settings.warnDirty)
warn("will not write lock file of flake '%s' because it has a mutable input", topRef); warn("will not write lock file of flake '%s' because it has a mutable input", topRef);
} else { } else {
if (!lockFlags.updateLockFile) if (!lockFlags.updateLockFile)
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef); throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock"; auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
auto path = *sourcePath + "/" + relPath; auto path = *sourcePath + "/" + relPath;
bool lockFileExists = pathExists(path); bool lockFileExists = pathExists(path);
if (lockFileExists) { if (lockFileExists) {
auto s = chomp(diff); auto s = chomp(diff);
if (s.empty()) if (s.empty())
warn("updating lock file '%s'", path); warn("updating lock file '%s'", path);
else else
warn("updating lock file '%s':\n%s", path, s); warn("updating lock file '%s':\n%s", path, s);
} else } else
warn("creating lock file '%s'", path); warn("creating lock file '%s'", path);
newLockFile.write(path); newLockFile.write(path);
topRef.input.markChangedFile( topRef.input.markChangedFile(
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock", (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
lockFlags.commitLockFile lockFlags.commitLockFile
? std::optional<std::string>(fmt("%s: %s\n\nFlake input changes:\n\n%s", ? std::optional<std::string>(fmt("%s: %s\n\nFlake input changes:\n\n%s",
relPath, lockFileExists ? "Update" : "Add", diff)) relPath, lockFileExists ? "Update" : "Add", diff))
: std::nullopt); : std::nullopt);
/* Rewriting the lockfile changed the top-level /* Rewriting the lockfile changed the top-level
repo, so we should re-read it. FIXME: we could repo, so we should re-read it. FIXME: we could
also just clear the 'rev' field... */ also just clear the 'rev' field... */
auto prevLockedRef = flake.lockedRef; auto prevLockedRef = flake.lockedRef;
FlakeCache dummyCache; FlakeCache dummyCache;
flake = getFlake(state, topRef, lockFlags.useRegistries, dummyCache); flake = getFlake(state, topRef, lockFlags.useRegistries, dummyCache);
if (lockFlags.commitLockFile && if (lockFlags.commitLockFile &&
flake.lockedRef.input.getRev() && flake.lockedRef.input.getRev() &&
prevLockedRef.input.getRev() != flake.lockedRef.input.getRev()) prevLockedRef.input.getRev() != flake.lockedRef.input.getRev())
warn("committed new revision '%s'", flake.lockedRef.input.getRev()->gitRev()); warn("committed new revision '%s'", flake.lockedRef.input.getRev()->gitRev());
/* Make sure that we picked up the change, /* Make sure that we picked up the change,
i.e. the tree should usually be dirty i.e. the tree should usually be dirty
now. Corner case: we could have reverted from a now. Corner case: we could have reverted from a
dirty to a clean tree! */ dirty to a clean tree! */
if (flake.lockedRef.input == prevLockedRef.input if (flake.lockedRef.input == prevLockedRef.input
&& !flake.lockedRef.input.isImmutable()) && !flake.lockedRef.input.isImmutable())
throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef); throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef);
} }
} else
throw Error("cannot write modified lock file of flake '%s' (use '--no-write-lock-file' to ignore)", topRef);
} else } else
throw Error("cannot write modified lock file of flake '%s' (use '--no-write-lock-file' to ignore)", topRef); warn("not writing modified lock file of flake '%s':\n%s", topRef, chomp(diff));
} else }
warn("not writing modified lock file of flake '%s':\n%s", topRef, chomp(diff));
}
return LockedFlake { .flake = std::move(flake), .lockFile = std::move(newLockFile) }; return LockedFlake { .flake = std::move(flake), .lockFile = std::move(newLockFile) };
} catch (Error & e) {
e.addTrace({}, "while updating the lock file of flake '%s'", flake.lockedRef.to_string());
throw;
}
} }
void callFlake(EvalState & state, void callFlake(EvalState & state,

View file

@ -17,23 +17,55 @@ struct FlakeInput;
typedef std::map<FlakeId, FlakeInput> FlakeInputs; typedef std::map<FlakeId, FlakeInput> FlakeInputs;
/* FlakeInput is the 'Flake'-level parsed form of the "input" entries
* in the flake file.
*
* A FlakeInput is normally constructed by the 'parseFlakeInput'
* function which parses the input specification in the '.flake' file
* to create a 'FlakeRef' (a fetcher, the fetcher-specific
* representation of the input specification, and possibly the fetched
* local store path result) and then creating this FlakeInput to hold
* that FlakeRef, along with anything that might override that
* FlakeRef (like command-line overrides or "follows" specifications).
*
* A FlakeInput is also sometimes constructed directly from a FlakeRef
* instead of starting at the flake-file input specification
* (e.g. overrides, follows, and implicit inputs).
*
* A FlakeInput will usually have one of either "ref" or "follows"
* set. If not otherwise specified, a "ref" will be generated to a
* 'type="indirect"' flake, which is treated as simply the name of a
* flake to be resolved in the registry.
*/
struct FlakeInput struct FlakeInput
{ {
std::optional<FlakeRef> ref; std::optional<FlakeRef> ref;
bool isFlake = true; bool isFlake = true; // true = process flake to get outputs, false = (fetched) static source path
std::optional<InputPath> follows; std::optional<InputPath> follows;
bool absolute = false; // whether 'follows' is relative to the flake root bool absolute = false; // whether 'follows' is relative to the flake root
FlakeInputs overrides; FlakeInputs overrides;
}; };
struct ConfigFile
{
using ConfigValue = std::variant<std::string, int64_t, Explicit<bool>, std::vector<std::string>>;
std::map<std::string, ConfigValue> settings;
void apply();
};
/* The contents of a flake.nix file. */
struct Flake struct Flake
{ {
FlakeRef originalRef; FlakeRef originalRef; // the original flake specification (by the user)
FlakeRef resolvedRef; FlakeRef resolvedRef; // registry references and caching resolved to the specific underlying flake
FlakeRef lockedRef; FlakeRef lockedRef; // the specific local store result of invoking the fetcher
std::optional<std::string> description; std::optional<std::string> description;
std::shared_ptr<const fetchers::Tree> sourceInfo; std::shared_ptr<const fetchers::Tree> sourceInfo;
FlakeInputs inputs; FlakeInputs inputs;
ConfigFile config; // 'nixConfig' attribute
~Flake(); ~Flake();
}; };

View file

@ -12,10 +12,33 @@ class Store;
typedef std::string FlakeId; typedef std::string FlakeId;
/* A flake reference specifies how to fetch a flake or raw source
* (e.g. from a Git repository). It is created from a URL-like syntax
* (e.g. 'github:NixOS/patchelf'), an attrset representation (e.g. '{
* type="github"; owner = "NixOS"; repo = "patchelf"; }'), or a local
* path.
*
* Each flake will have a number of FlakeRef objects: one for each
* input to the flake.
*
* The normal method of constructing a FlakeRef is by starting with an
* input description (usually the attrs or a url from the flake file),
* locating a fetcher for that input, and then capturing the Input
* object that fetcher generates (usually via
* FlakeRef::fromAttrs(attrs) or parseFlakeRef(url) calls).
*
* The actual fetch not have been performed yet (i.e. a FlakeRef may
* be lazy), but the fetcher can be invoked at any time via the
* FlakeRef to ensure the store is populated with this input.
*/
struct FlakeRef struct FlakeRef
{ {
/* fetcher-specific representation of the input, sufficient to
perform the fetch operation. */
fetchers::Input input; fetchers::Input input;
/* sub-path within the fetched input that represents this input */
Path subdir; Path subdir;
bool operator==(const FlakeRef & other) const; bool operator==(const FlakeRef & other) const;

View file

@ -34,7 +34,8 @@ LockedNode::LockedNode(const nlohmann::json & json)
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) , isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
{ {
if (!lockedRef.input.isImmutable()) if (!lockedRef.input.isImmutable())
throw Error("lockfile contains mutable lock '%s'", attrsToJson(lockedRef.input.toAttrs())); throw Error("lockfile contains mutable lock '%s'",
fetchers::attrsToJSON(lockedRef.input.toAttrs()));
} }
StorePath LockedNode::computeStorePath(Store & store) const StorePath LockedNode::computeStorePath(Store & store) const
@ -77,7 +78,7 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path)
{ {
if (jsonNode.find("inputs") == jsonNode.end()) return; if (jsonNode.find("inputs") == jsonNode.end()) return;
for (auto & i : jsonNode["inputs"].items()) { for (auto & i : jsonNode["inputs"].items()) {
if (i.value().is_array()) { if (i.value().is_array()) { // FIXME: remove, obsolete
InputPath path; InputPath path;
for (auto & j : i.value()) for (auto & j : i.value())
path.push_back(j); path.push_back(j);
@ -86,10 +87,13 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path)
std::string inputKey = i.value(); std::string inputKey = i.value();
auto k = nodeMap.find(inputKey); auto k = nodeMap.find(inputKey);
if (k == nodeMap.end()) { if (k == nodeMap.end()) {
auto jsonNode2 = json["nodes"][inputKey]; auto nodes = json["nodes"];
auto input = std::make_shared<LockedNode>(jsonNode2); auto jsonNode2 = nodes.find(inputKey);
if (jsonNode2 == nodes.end())
throw Error("lock file references missing node '%s'", inputKey);
auto input = std::make_shared<LockedNode>(*jsonNode2);
k = nodeMap.insert_or_assign(inputKey, input).first; k = nodeMap.insert_or_assign(inputKey, input).first;
getInputs(*input, jsonNode2); getInputs(*input, *jsonNode2);
} }
if (auto child = std::dynamic_pointer_cast<LockedNode>(k->second)) if (auto child = std::dynamic_pointer_cast<LockedNode>(k->second))
node.inputs.insert_or_assign(i.key(), child); node.inputs.insert_or_assign(i.key(), child);
@ -110,7 +114,7 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path)
// a bit since we don't need to worry about cycles. // a bit since we don't need to worry about cycles.
} }
nlohmann::json LockFile::toJson() const nlohmann::json LockFile::toJSON() const
{ {
nlohmann::json nodes; nlohmann::json nodes;
std::unordered_map<std::shared_ptr<const Node>, std::string> nodeKeys; std::unordered_map<std::shared_ptr<const Node>, std::string> nodeKeys;
@ -154,8 +158,8 @@ nlohmann::json LockFile::toJson() const
} }
if (auto lockedNode = std::dynamic_pointer_cast<const LockedNode>(node)) { if (auto lockedNode = std::dynamic_pointer_cast<const LockedNode>(node)) {
n["original"] = fetchers::attrsToJson(lockedNode->originalRef.toAttrs()); n["original"] = fetchers::attrsToJSON(lockedNode->originalRef.toAttrs());
n["locked"] = fetchers::attrsToJson(lockedNode->lockedRef.toAttrs()); n["locked"] = fetchers::attrsToJSON(lockedNode->lockedRef.toAttrs());
if (!lockedNode->isFlake) n["flake"] = false; if (!lockedNode->isFlake) n["flake"] = false;
} }
@ -174,7 +178,7 @@ nlohmann::json LockFile::toJson() const
std::string LockFile::to_string() const std::string LockFile::to_string() const
{ {
return toJson().dump(2); return toJSON().dump(2);
} }
LockFile LockFile::read(const Path & path) LockFile LockFile::read(const Path & path)
@ -185,7 +189,7 @@ LockFile LockFile::read(const Path & path)
std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile) std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
{ {
stream << lockFile.toJson().dump(2); stream << lockFile.toJSON().dump(2);
return stream; return stream;
} }
@ -223,7 +227,7 @@ bool LockFile::isImmutable() const
bool LockFile::operator ==(const LockFile & other) const bool LockFile::operator ==(const LockFile & other) const
{ {
// FIXME: slow // FIXME: slow
return toJson() == other.toJson(); return toJSON() == other.toJSON();
} }
InputPath parseInputPath(std::string_view s) InputPath parseInputPath(std::string_view s)

View file

@ -52,7 +52,7 @@ struct LockFile
LockFile() {}; LockFile() {};
LockFile(const nlohmann::json & json, const Path & path); LockFile(const nlohmann::json & json, const Path & path);
nlohmann::json toJson() const; nlohmann::json toJSON() const;
std::string to_string() const; std::string to_string() const;

View file

@ -128,7 +128,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
if (!outTI->isList()) throw errMsg; if (!outTI->isList()) throw errMsg;
Outputs result; Outputs result;
for (auto i = outTI->listElems(); i != outTI->listElems() + outTI->listSize(); ++i) { for (auto i = outTI->listElems(); i != outTI->listElems() + outTI->listSize(); ++i) {
if ((*i)->type != tString) throw errMsg; if ((*i)->type() != nString) throw errMsg;
auto out = outputs.find((*i)->string.s); auto out = outputs.find((*i)->string.s);
if (out == outputs.end()) throw errMsg; if (out == outputs.end()) throw errMsg;
result.insert(*out); result.insert(*out);
@ -172,20 +172,20 @@ StringSet DrvInfo::queryMetaNames()
bool DrvInfo::checkMeta(Value & v) bool DrvInfo::checkMeta(Value & v)
{ {
state->forceValue(v); state->forceValue(v);
if (v.isList()) { if (v.type() == nList) {
for (unsigned int n = 0; n < v.listSize(); ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
if (!checkMeta(*v.listElems()[n])) return false; if (!checkMeta(*v.listElems()[n])) return false;
return true; return true;
} }
else if (v.type == tAttrs) { else if (v.type() == nAttrs) {
Bindings::iterator i = v.attrs->find(state->sOutPath); Bindings::iterator i = v.attrs->find(state->sOutPath);
if (i != v.attrs->end()) return false; if (i != v.attrs->end()) return false;
for (auto & i : *v.attrs) for (auto & i : *v.attrs)
if (!checkMeta(*i.value)) return false; if (!checkMeta(*i.value)) return false;
return true; return true;
} }
else return v.type == tInt || v.type == tBool || v.type == tString || else return v.type() == nInt || v.type() == nBool || v.type() == nString ||
v.type == tFloat; v.type() == nFloat;
} }
@ -201,7 +201,7 @@ Value * DrvInfo::queryMeta(const string & name)
string DrvInfo::queryMetaString(const string & name) string DrvInfo::queryMetaString(const string & name)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v || v->type != tString) return ""; if (!v || v->type() != nString) return "";
return v->string.s; return v->string.s;
} }
@ -210,12 +210,12 @@ NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type == tInt) return v->integer; if (v->type() == nInt) return v->integer;
if (v->type == tString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
integer meta fields. */ integer meta fields. */
NixInt n; if (auto n = string2Int<NixInt>(v->string.s))
if (string2Int(v->string.s, n)) return n; return *n;
} }
return def; return def;
} }
@ -224,12 +224,12 @@ NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type == tFloat) return v->fpoint; if (v->type() == nFloat) return v->fpoint;
if (v->type == tString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
float meta fields. */ float meta fields. */
NixFloat n; if (auto n = string2Float<NixFloat>(v->string.s))
if (string2Float(v->string.s, n)) return n; return *n;
} }
return def; return def;
} }
@ -239,8 +239,8 @@ bool DrvInfo::queryMetaBool(const string & name, bool def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type == tBool) return v->boolean; if (v->type() == nBool) return v->boolean;
if (v->type == tString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
Boolean meta fields. */ Boolean meta fields. */
if (strcmp(v->string.s, "true") == 0) return true; if (strcmp(v->string.s, "true") == 0) return true;
@ -331,7 +331,7 @@ static void getDerivations(EvalState & state, Value & vIn,
/* Process the expression. */ /* Process the expression. */
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ; if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
else if (v.type == tAttrs) { else if (v.type() == nAttrs) {
/* !!! undocumented hackery to support combining channels in /* !!! undocumented hackery to support combining channels in
nix-env.cc. */ nix-env.cc. */
@ -353,7 +353,7 @@ static void getDerivations(EvalState & state, Value & vIn,
/* If the value of this attribute is itself a set, /* If the value of this attribute is itself a set,
should we recurse into it? => Only if it has a should we recurse into it? => Only if it has a
`recurseForDerivations = true' attribute. */ `recurseForDerivations = true' attribute. */
if (i->value->type == tAttrs) { if (i->value->type() == nAttrs) {
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos)) if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
@ -362,7 +362,7 @@ static void getDerivations(EvalState & state, Value & vIn,
} }
} }
else if (v.isList()) { else if (v.type() == nList) {
for (unsigned int n = 0; n < v.listSize(); ++n) { for (unsigned int n = 0; n < v.listSize(); ++n) {
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures)) if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))

View file

@ -12,6 +12,10 @@
%{ %{
#ifdef __clang__
#pragma clang diagnostic ignored "-Wunneeded-internal-declaration"
#endif
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "nixexpr.hh" #include "nixexpr.hh"

View file

@ -15,7 +15,7 @@ libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/lib
libexpr_LIBS = libutil libstore libfetchers libexpr_LIBS = libutil libstore libfetchers
libexpr_LDFLAGS = libexpr_LDFLAGS = -lboost_context
ifneq ($(OS), FreeBSD) ifneq ($(OS), FreeBSD)
libexpr_LDFLAGS += -ldl libexpr_LDFLAGS += -ldl
endif endif
@ -35,13 +35,11 @@ $(d)/lexer-tab.cc $(d)/lexer-tab.hh: $(d)/lexer.l
clean-files += $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh clean-files += $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh
dist-files += $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh
$(eval $(call install-file-in, $(d)/nix-expr.pc, $(prefix)/lib/pkgconfig, 0644)) $(eval $(call install-file-in, $(d)/nix-expr.pc, $(prefix)/lib/pkgconfig, 0644))
$(foreach i, $(wildcard src/libexpr/flake/*.hh), \ $(foreach i, $(wildcard src/libexpr/flake/*.hh), \
$(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644))) $(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644)))
$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/primops/derivation.nix.gen.hh $(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh
$(d)/flake/flake.cc: $(d)/flake/call-flake.nix.gen.hh $(d)/flake/flake.cc: $(d)/flake/call-flake.nix.gen.hh

View file

@ -284,7 +284,7 @@ void ExprVar::bindVars(const StaticEnv & env)
"undefined variable" error now. */ "undefined variable" error now. */
if (withLevel == -1) if (withLevel == -1)
throw UndefinedVarError({ throw UndefinedVarError({
.hint = hintfmt("undefined variable '%1%'", name), .msg = hintfmt("undefined variable '%1%'", name),
.errPos = pos .errPos = pos
}); });
fromWith = true; fromWith = true;

View file

@ -17,6 +17,7 @@ MakeError(ThrownError, AssertionError);
MakeError(Abort, EvalError); MakeError(Abort, EvalError);
MakeError(TypeError, EvalError); MakeError(TypeError, EvalError);
MakeError(UndefinedVarError, Error); MakeError(UndefinedVarError, Error);
MakeError(MissingArgumentError, EvalError);
MakeError(RestrictedPathError, Error); MakeError(RestrictedPathError, Error);
@ -129,7 +130,7 @@ struct ExprPath : Expr
{ {
string s; string s;
Value v; Value v;
ExprPath(const string & s) : s(s) { mkPathNoCopy(v, this->s.c_str()); }; ExprPath(const string & s) : s(s) { v.mkPath(this->s.c_str()); };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
@ -238,7 +239,7 @@ struct ExprLambda : Expr
{ {
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end()) if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
throw ParseError({ throw ParseError({
.hint = hintfmt("duplicate formal function argument '%1%'", arg), .msg = hintfmt("duplicate formal function argument '%1%'", arg),
.errPos = pos .errPos = pos
}); });
}; };

View file

@ -32,7 +32,7 @@ namespace nix {
Path basePath; Path basePath;
Symbol file; Symbol file;
FileOrigin origin; FileOrigin origin;
ErrorInfo error; std::optional<ErrorInfo> error;
Symbol sLetBody; Symbol sLetBody;
ParseData(EvalState & state) ParseData(EvalState & state)
: state(state) : state(state)
@ -66,8 +66,8 @@ namespace nix {
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos) static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
{ {
throw ParseError({ throw ParseError({
.hint = hintfmt("attribute '%1%' already defined at %2%", .msg = hintfmt("attribute '%1%' already defined at %2%",
showAttrPath(attrPath), prevPos), showAttrPath(attrPath), prevPos),
.errPos = pos .errPos = pos
}); });
} }
@ -75,7 +75,7 @@ static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prev
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos) static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{ {
throw ParseError({ throw ParseError({
.hint = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos), .msg = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos),
.errPos = pos .errPos = pos
}); });
} }
@ -146,7 +146,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
{ {
if (!formals->argNames.insert(formal.name).second) if (!formals->argNames.insert(formal.name).second)
throw ParseError({ throw ParseError({
.hint = hintfmt("duplicate formal function argument '%1%'", .msg = hintfmt("duplicate formal function argument '%1%'",
formal.name), formal.name),
.errPos = pos .errPos = pos
}); });
@ -258,7 +258,7 @@ static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error) void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
{ {
data->error = { data->error = {
.hint = hintfmt(error), .msg = hintfmt(error),
.errPos = makeCurPos(*loc, data) .errPos = makeCurPos(*loc, data)
}; };
} }
@ -338,7 +338,7 @@ expr_function
| LET binds IN expr_function | LET binds IN expr_function
{ if (!$2->dynamicAttrs.empty()) { if (!$2->dynamicAttrs.empty())
throw ParseError({ throw ParseError({
.hint = hintfmt("dynamic attributes not allowed in let"), .msg = hintfmt("dynamic attributes not allowed in let"),
.errPos = CUR_POS .errPos = CUR_POS
}); });
$$ = new ExprLet($2, $4); $$ = new ExprLet($2, $4);
@ -418,7 +418,7 @@ expr_simple
static bool noURLLiterals = settings.isExperimentalFeatureEnabled("no-url-literals"); static bool noURLLiterals = settings.isExperimentalFeatureEnabled("no-url-literals");
if (noURLLiterals) if (noURLLiterals)
throw ParseError({ throw ParseError({
.hint = hintfmt("URL literals are disabled"), .msg = hintfmt("URL literals are disabled"),
.errPos = CUR_POS .errPos = CUR_POS
}); });
$$ = new ExprString(data->symbols.create($1)); $$ = new ExprString(data->symbols.create($1));
@ -491,7 +491,7 @@ attrs
delete str; delete str;
} else } else
throw ParseError({ throw ParseError({
.hint = hintfmt("dynamic attributes not allowed in inherit"), .msg = hintfmt("dynamic attributes not allowed in inherit"),
.errPos = makeCurPos(@2, data) .errPos = makeCurPos(@2, data)
}); });
} }
@ -576,7 +576,7 @@ Expr * EvalState::parse(const char * text, FileOrigin origin,
ParseData data(*this); ParseData data(*this);
data.origin = origin; data.origin = origin;
switch (origin) { switch (origin) {
case foFile: case foFile:
data.file = data.symbols.create(path); data.file = data.symbols.create(path);
break; break;
case foStdin: case foStdin:
@ -593,7 +593,7 @@ Expr * EvalState::parse(const char * text, FileOrigin origin,
int res = yyparse(scanner, &data); int res = yyparse(scanner, &data);
yylex_destroy(scanner); yylex_destroy(scanner);
if (res) throw ParseError(data.error); if (res) throw ParseError(data.error.value());
data.result->bindVars(staticEnv); data.result->bindVars(staticEnv);
@ -698,8 +698,12 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
Path res = r.second + suffix; Path res = r.second + suffix;
if (pathExists(res)) return canonPath(res); if (pathExists(res)) return canonPath(res);
} }
if (hasPrefix(path, "nix/"))
return corepkgsPrefix + path.substr(4);
throw ThrownError({ throw ThrownError({
.hint = hintfmt(evalSettings.pureEval .msg = hintfmt(evalSettings.pureEval
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)" ? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)", : "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
path), path),
@ -721,8 +725,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
store, resolveUri(elem.second), "source", false).first.storePath) }; store, resolveUri(elem.second), "source", false).first.storePath) };
} catch (FileTransferError & e) { } catch (FileTransferError & e) {
logWarning({ logWarning({
.name = "Entry download", .msg = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
.hint = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
}); });
res = { false, "" }; res = { false, "" };
} }
@ -732,8 +735,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
res = { true, path }; res = { true, path };
else { else {
logWarning({ logWarning({
.name = "Entry not found", .msg = hintfmt("warning: Nix search path entry '%1%' does not exist, ignoring", elem.second)
.hint = hintfmt("warning: Nix search path entry '%1%' does not exist, ignoring", elem.second)
}); });
res = { false, "" }; res = { false, "" };
} }

View file

@ -115,9 +115,12 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
state.realiseContext(context); state.realiseContext(context);
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
throw EvalError({ throw EvalError({
.hint = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path), .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos .errPos = pos
}); });
} catch (Error & e) {
e.addTrace(pos, "while importing '%s'", path);
throw e;
} }
Path realPath = state.checkSourcePath(state.toRealPath(path, context)); Path realPath = state.checkSourcePath(state.toRealPath(path, context));
@ -164,7 +167,15 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
state.forceFunction(**fun, pos); state.forceFunction(**fun, pos);
mkApp(v, **fun, w); mkApp(v, **fun, w);
state.forceAttrs(v, pos); state.forceAttrs(v, pos);
} else { }
else if (path == corepkgsPrefix + "fetchurl.nix") {
state.eval(state.parseExprFromString(
#include "fetchurl.nix.gen.hh"
, "/"), v);
}
else {
if (!vScope) if (!vScope)
state.evalFile(realPath, v); state.evalFile(realPath, v);
else { else {
@ -274,7 +285,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
state.realiseContext(context); state.realiseContext(context);
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
throw EvalError({ throw EvalError({
.hint = hintfmt( .msg = hintfmt(
"cannot import '%1%', since path '%2%' is not valid", "cannot import '%1%', since path '%2%' is not valid",
path, e.path), path, e.path),
.errPos = pos .errPos = pos
@ -314,7 +325,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
auto count = args[0]->listSize(); auto count = args[0]->listSize();
if (count == 0) { if (count == 0) {
throw EvalError({ throw EvalError({
.hint = hintfmt("at least one argument to 'exec' required"), .msg = hintfmt("at least one argument to 'exec' required"),
.errPos = pos .errPos = pos
}); });
} }
@ -328,7 +339,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
state.realiseContext(context); state.realiseContext(context);
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
throw EvalError({ throw EvalError({
.hint = hintfmt("cannot execute '%1%', since path '%2%' is not valid", .msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
program, e.path), program, e.path),
.errPos = pos .errPos = pos
}); });
@ -356,24 +367,20 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
string t; string t;
switch (args[0]->type) { switch (args[0]->type()) {
case tInt: t = "int"; break; case nInt: t = "int"; break;
case tBool: t = "bool"; break; case nBool: t = "bool"; break;
case tString: t = "string"; break; case nString: t = "string"; break;
case tPath: t = "path"; break; case nPath: t = "path"; break;
case tNull: t = "null"; break; case nNull: t = "null"; break;
case tAttrs: t = "set"; break; case nAttrs: t = "set"; break;
case tList1: case tList2: case tListN: t = "list"; break; case nList: t = "list"; break;
case tLambda: case nFunction: t = "lambda"; break;
case tPrimOp: case nExternal:
case tPrimOpApp:
t = "lambda";
break;
case tExternal:
t = args[0]->external->typeOf(); t = args[0]->external->typeOf();
break; break;
case tFloat: t = "float"; break; case nFloat: t = "float"; break;
default: abort(); case nThunk: abort();
} }
mkString(v, state.symbols.create(t)); mkString(v, state.symbols.create(t));
} }
@ -393,7 +400,7 @@ static RegisterPrimOp primop_typeOf({
static void prim_isNull(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isNull(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
mkBool(v, args[0]->type == tNull); mkBool(v, args[0]->type() == nNull);
} }
static RegisterPrimOp primop_isNull({ static RegisterPrimOp primop_isNull({
@ -413,18 +420,7 @@ static RegisterPrimOp primop_isNull({
static void prim_isFunction(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isFunction(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
bool res; mkBool(v, args[0]->type() == nFunction);
switch (args[0]->type) {
case tLambda:
case tPrimOp:
case tPrimOpApp:
res = true;
break;
default:
res = false;
break;
}
mkBool(v, res);
} }
static RegisterPrimOp primop_isFunction({ static RegisterPrimOp primop_isFunction({
@ -440,7 +436,7 @@ static RegisterPrimOp primop_isFunction({
static void prim_isInt(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isInt(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
mkBool(v, args[0]->type == tInt); mkBool(v, args[0]->type() == nInt);
} }
static RegisterPrimOp primop_isInt({ static RegisterPrimOp primop_isInt({
@ -456,7 +452,7 @@ static RegisterPrimOp primop_isInt({
static void prim_isFloat(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isFloat(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
mkBool(v, args[0]->type == tFloat); mkBool(v, args[0]->type() == nFloat);
} }
static RegisterPrimOp primop_isFloat({ static RegisterPrimOp primop_isFloat({
@ -472,7 +468,7 @@ static RegisterPrimOp primop_isFloat({
static void prim_isString(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
mkBool(v, args[0]->type == tString); mkBool(v, args[0]->type() == nString);
} }
static RegisterPrimOp primop_isString({ static RegisterPrimOp primop_isString({
@ -488,7 +484,7 @@ static RegisterPrimOp primop_isString({
static void prim_isBool(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isBool(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
mkBool(v, args[0]->type == tBool); mkBool(v, args[0]->type() == nBool);
} }
static RegisterPrimOp primop_isBool({ static RegisterPrimOp primop_isBool({
@ -504,7 +500,7 @@ static RegisterPrimOp primop_isBool({
static void prim_isPath(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isPath(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
mkBool(v, args[0]->type == tPath); mkBool(v, args[0]->type() == nPath);
} }
static RegisterPrimOp primop_isPath({ static RegisterPrimOp primop_isPath({
@ -520,20 +516,20 @@ struct CompareValues
{ {
bool operator () (const Value * v1, const Value * v2) const bool operator () (const Value * v1, const Value * v2) const
{ {
if (v1->type == tFloat && v2->type == tInt) if (v1->type() == nFloat && v2->type() == nInt)
return v1->fpoint < v2->integer; return v1->fpoint < v2->integer;
if (v1->type == tInt && v2->type == tFloat) if (v1->type() == nInt && v2->type() == nFloat)
return v1->integer < v2->fpoint; return v1->integer < v2->fpoint;
if (v1->type != v2->type) if (v1->type() != v2->type())
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2)); throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
switch (v1->type) { switch (v1->type()) {
case tInt: case nInt:
return v1->integer < v2->integer; return v1->integer < v2->integer;
case tFloat: case nFloat:
return v1->fpoint < v2->fpoint; return v1->fpoint < v2->fpoint;
case tString: case nString:
return strcmp(v1->string.s, v2->string.s) < 0; return strcmp(v1->string.s, v2->string.s) < 0;
case tPath: case nPath:
return strcmp(v1->path, v2->path) < 0; return strcmp(v1->path, v2->path) < 0;
default: default:
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2)); throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
@ -558,7 +554,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
args[0]->attrs->find(state.symbols.create("startSet")); args[0]->attrs->find(state.symbols.create("startSet"));
if (startSet == args[0]->attrs->end()) if (startSet == args[0]->attrs->end())
throw EvalError({ throw EvalError({
.hint = hintfmt("attribute 'startSet' required"), .msg = hintfmt("attribute 'startSet' required"),
.errPos = pos .errPos = pos
}); });
state.forceList(*startSet->value, pos); state.forceList(*startSet->value, pos);
@ -572,7 +568,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
args[0]->attrs->find(state.symbols.create("operator")); args[0]->attrs->find(state.symbols.create("operator"));
if (op == args[0]->attrs->end()) if (op == args[0]->attrs->end())
throw EvalError({ throw EvalError({
.hint = hintfmt("attribute 'operator' required"), .msg = hintfmt("attribute 'operator' required"),
.errPos = pos .errPos = pos
}); });
state.forceValue(*op->value, pos); state.forceValue(*op->value, pos);
@ -594,7 +590,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
e->attrs->find(state.symbols.create("key")); e->attrs->find(state.symbols.create("key"));
if (key == e->attrs->end()) if (key == e->attrs->end())
throw EvalError({ throw EvalError({
.hint = hintfmt("attribute 'key' required"), .msg = hintfmt("attribute 'key' required"),
.errPos = pos .errPos = pos
}); });
state.forceValue(*key->value, pos); state.forceValue(*key->value, pos);
@ -700,10 +696,14 @@ static RegisterPrimOp primop_tryEval({
Try to shallowly evaluate *e*. Return a set containing the Try to shallowly evaluate *e*. Return a set containing the
attributes `success` (`true` if *e* evaluated successfully, attributes `success` (`true` if *e* evaluated successfully,
`false` if an error was thrown) and `value`, equalling *e* if `false` if an error was thrown) and `value`, equalling *e* if
successful and `false` otherwise. Note that this doesn't evaluate successful and `false` otherwise. `tryEval` will only prevent
*e* deeply, so ` let e = { x = throw ""; }; in (builtins.tryEval errors created by `throw` or `assert` from being thrown.
e).success ` will be `true`. Using ` builtins.deepSeq ` one can Errors `tryEval` will not catch are for example those created
get the expected result: `let e = { x = throw ""; }; in by `abort` and type errors generated by builtins. Also note that
this doesn't evaluate *e* deeply, so `let e = { x = throw ""; };
in (builtins.tryEval e).success` will be `true`. Using
`builtins.deepSeq` one can get the expected result:
`let e = { x = throw ""; }; in
(builtins.tryEval (builtins.deepSeq e e)).success` will be (builtins.tryEval (builtins.deepSeq e e)).success` will be
`false`. `false`.
)", )",
@ -777,7 +777,7 @@ static RegisterPrimOp primop_deepSeq({
static void prim_trace(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_trace(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
if (args[0]->type == tString) if (args[0]->type() == nString)
printError("trace: %1%", args[0]->string.s); printError("trace: %1%", args[0]->string.s);
else else
printError("trace: %1%", *args[0]); printError("trace: %1%", *args[0]);
@ -817,7 +817,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
Bindings::iterator attr = args[0]->attrs->find(state.sName); Bindings::iterator attr = args[0]->attrs->find(state.sName);
if (attr == args[0]->attrs->end()) if (attr == args[0]->attrs->end())
throw EvalError({ throw EvalError({
.hint = hintfmt("required attribute 'name' missing"), .msg = hintfmt("required attribute 'name' missing"),
.errPos = pos .errPos = pos
}); });
string drvName; string drvName;
@ -866,7 +866,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat; else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else else
throw EvalError({ throw EvalError({
.hint = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s), .msg = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
.errPos = posDrvName .errPos = posDrvName
}); });
}; };
@ -876,7 +876,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
for (auto & j : ss) { for (auto & j : ss) {
if (outputs.find(j) != outputs.end()) if (outputs.find(j) != outputs.end())
throw EvalError({ throw EvalError({
.hint = hintfmt("duplicate derivation output '%1%'", j), .msg = hintfmt("duplicate derivation output '%1%'", j),
.errPos = posDrvName .errPos = posDrvName
}); });
/* !!! Check whether j is a valid attribute /* !!! Check whether j is a valid attribute
@ -886,14 +886,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
the resulting set. */ the resulting set. */
if (j == "drv") if (j == "drv")
throw EvalError({ throw EvalError({
.hint = hintfmt("invalid derivation output name 'drv'" ), .msg = hintfmt("invalid derivation output name 'drv'" ),
.errPos = posDrvName .errPos = posDrvName
}); });
outputs.insert(j); outputs.insert(j);
} }
if (outputs.empty()) if (outputs.empty())
throw EvalError({ throw EvalError({
.hint = hintfmt("derivation cannot have an empty set of outputs"), .msg = hintfmt("derivation cannot have an empty set of outputs"),
.errPos = posDrvName .errPos = posDrvName
}); });
}; };
@ -902,7 +902,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (ignoreNulls) { if (ignoreNulls) {
state.forceValue(*i->value, pos); state.forceValue(*i->value, pos);
if (i->value->type == tNull) continue; if (i->value->type() == nNull) continue;
} }
if (i->name == state.sContentAddressed) { if (i->name == state.sContentAddressed) {
@ -1014,20 +1014,20 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Do we have all required attributes? */ /* Do we have all required attributes? */
if (drv.builder == "") if (drv.builder == "")
throw EvalError({ throw EvalError({
.hint = hintfmt("required attribute 'builder' missing"), .msg = hintfmt("required attribute 'builder' missing"),
.errPos = posDrvName .errPos = posDrvName
}); });
if (drv.platform == "") if (drv.platform == "")
throw EvalError({ throw EvalError({
.hint = hintfmt("required attribute 'system' missing"), .msg = hintfmt("required attribute 'system' missing"),
.errPos = posDrvName .errPos = posDrvName
}); });
/* Check whether the derivation name is valid. */ /* Check whether the derivation name is valid. */
if (isDerivation(drvName)) if (isDerivation(drvName))
throw EvalError({ throw EvalError({
.hint = hintfmt("derivation names are not allowed to end in '%s'", drvExtension), .msg = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
.errPos = posDrvName .errPos = posDrvName
}); });
@ -1038,7 +1038,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
already content addressed. */ already content addressed. */
if (outputs.size() != 1 || *(outputs.begin()) != "out") if (outputs.size() != 1 || *(outputs.begin()) != "out")
throw Error({ throw Error({
.hint = hintfmt("multiple outputs are not supported in fixed-output derivations"), .msg = hintfmt("multiple outputs are not supported in fixed-output derivations"),
.errPos = posDrvName .errPos = posDrvName
}); });
@ -1095,18 +1095,35 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
// Regular, non-CA derivation should always return a single hash and not // Regular, non-CA derivation should always return a single hash and not
// hash per output. // hash per output.
Hash h = std::get<0>(hashDerivationModulo(*state.store, Derivation(drv), true)); auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true);
std::visit(overloaded {
[&](Hash h) {
for (auto & i : outputs) {
auto outPath = state.store->makeOutputPath(i, h, drvName);
drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign(i,
DerivationOutput {
.output = DerivationOutputInputAddressed {
.path = std::move(outPath),
},
});
}
},
[&](CaOutputHashes) {
// Shouldn't happen as the toplevel derivation is not CA.
assert(false);
},
[&](DeferredHash _) {
for (auto & i : outputs) {
drv.outputs.insert_or_assign(i,
DerivationOutput {
.output = DerivationOutputDeferred{},
});
}
},
},
hashModulo);
for (auto & i : outputs) {
auto outPath = state.store->makeOutputPath(i, h, drvName);
drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign(i,
DerivationOutput {
.output = DerivationOutputInputAddressed {
.path = std::move(outPath),
},
});
}
} }
/* Write the resulting term into the Nix store directory. */ /* Write the resulting term into the Nix store directory. */
@ -1121,9 +1138,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
However, we don't bother doing this for floating CA derivations because However, we don't bother doing this for floating CA derivations because
their "hash modulo" is indeterminate until built. */ their "hash modulo" is indeterminate until built. */
if (drv.type() != DerivationType::CAFloating) if (drv.type() != DerivationType::CAFloating) {
drvHashes.insert_or_assign(drvPath, auto h = hashDerivationModulo(*state.store, Derivation(drv), false);
hashDerivationModulo(*state.store, Derivation(drv), false)); drvHashes.lock()->insert_or_assign(drvPath, h);
}
state.mkAttrs(v, 1 + drv.outputs.size()); state.mkAttrs(v, 1 + drv.outputs.size());
mkString(*state.allocAttr(v, state.sDrvPath), drvPathS, {"=" + drvPathS}); mkString(*state.allocAttr(v, state.sDrvPath), drvPathS, {"=" + drvPathS});
@ -1206,7 +1224,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
if (!state.store->isStorePath(path)) path = canonPath(path, true); if (!state.store->isStorePath(path)) path = canonPath(path, true);
if (!state.store->isInStore(path)) if (!state.store->isInStore(path))
throw EvalError({ throw EvalError({
.hint = hintfmt("path '%1%' is not in the Nix store", path), .msg = hintfmt("path '%1%' is not in the Nix store", path),
.errPos = pos .errPos = pos
}); });
auto path2 = state.store->toStorePath(path).first; auto path2 = state.store->toStorePath(path).first;
@ -1242,7 +1260,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
state.realiseContext(context); state.realiseContext(context);
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
throw EvalError({ throw EvalError({
.hint = hintfmt( .msg = hintfmt(
"cannot check the existence of '%1%', since path '%2%' is not valid", "cannot check the existence of '%1%', since path '%2%' is not valid",
path, e.path), path, e.path),
.errPos = pos .errPos = pos
@ -1296,7 +1314,7 @@ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value
{ {
PathSet context; PathSet context;
Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false)); Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false));
if (args[0]->type == tPath) mkPath(v, dir.c_str()); else mkString(v, dir, context); if (args[0]->type() == nPath) mkPath(v, dir.c_str()); else mkString(v, dir, context);
} }
static RegisterPrimOp primop_dirOf({ static RegisterPrimOp primop_dirOf({
@ -1319,7 +1337,7 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
state.realiseContext(context); state.realiseContext(context);
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
throw EvalError({ throw EvalError({
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path), .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos .errPos = pos
}); });
} }
@ -1358,7 +1376,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
i = v2.attrs->find(state.symbols.create("path")); i = v2.attrs->find(state.symbols.create("path"));
if (i == v2.attrs->end()) if (i == v2.attrs->end())
throw EvalError({ throw EvalError({
.hint = hintfmt("attribute 'path' missing"), .msg = hintfmt("attribute 'path' missing"),
.errPos = pos .errPos = pos
}); });
@ -1369,7 +1387,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
state.realiseContext(context); state.realiseContext(context);
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
throw EvalError({ throw EvalError({
.hint = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path), .msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos .errPos = pos
}); });
} }
@ -1395,7 +1413,7 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
throw Error({ throw Error({
.hint = hintfmt("unknown hash type '%1%'", type), .msg = hintfmt("unknown hash type '%1%'", type),
.errPos = pos .errPos = pos
}); });
@ -1425,7 +1443,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
state.realiseContext(ctx); state.realiseContext(ctx);
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
throw EvalError({ throw EvalError({
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path), .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos .errPos = pos
}); });
} }
@ -1437,7 +1455,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name)); Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name));
if (ent.type == DT_UNKNOWN) if (ent.type == DT_UNKNOWN)
ent.type = getFileType(path + "/" + ent.name); ent.type = getFileType(path + "/" + ent.name);
mkStringNoCopy(*ent_val, ent_val->mkString(
ent.type == DT_REG ? "regular" : ent.type == DT_REG ? "regular" :
ent.type == DT_DIR ? "directory" : ent.type == DT_DIR ? "directory" :
ent.type == DT_LNK ? "symlink" : ent.type == DT_LNK ? "symlink" :
@ -1609,7 +1627,12 @@ static RegisterPrimOp primop_toJSON({
static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string s = state.forceStringNoCtx(*args[0], pos); string s = state.forceStringNoCtx(*args[0], pos);
parseJSON(state, s, v); try {
parseJSON(state, s, v);
} catch (JSONParseError &e) {
e.addTrace(pos, "while decoding a JSON string");
throw e;
}
} }
static RegisterPrimOp primop_fromJSON({ static RegisterPrimOp primop_fromJSON({
@ -1640,7 +1663,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
for (auto path : context) { for (auto path : context) {
if (path.at(0) != '/') if (path.at(0) != '/')
throw EvalError( { throw EvalError( {
.hint = hintfmt( .msg = hintfmt(
"in 'toFile': the file named '%1%' must not contain a reference " "in 'toFile': the file named '%1%' must not contain a reference "
"to a derivation but contains (%2%)", "to a derivation but contains (%2%)",
name, path), name, path),
@ -1797,14 +1820,14 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
Path path = state.coerceToPath(pos, *args[1], context); Path path = state.coerceToPath(pos, *args[1], context);
if (!context.empty()) if (!context.empty())
throw EvalError({ throw EvalError({
.hint = hintfmt("string '%1%' cannot refer to other paths", path), .msg = hintfmt("string '%1%' cannot refer to other paths", path),
.errPos = pos .errPos = pos
}); });
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
if (args[0]->type != tLambda) if (args[0]->type() != nFunction)
throw TypeError({ throw TypeError({
.hint = hintfmt( .msg = hintfmt(
"first argument in call to 'filterSource' is not a function but %1%", "first argument in call to 'filterSource' is not a function but %1%",
showType(*args[0])), showType(*args[0])),
.errPos = pos .errPos = pos
@ -1871,7 +1894,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
path = state.coerceToPath(*attr.pos, *attr.value, context); path = state.coerceToPath(*attr.pos, *attr.value, context);
if (!context.empty()) if (!context.empty())
throw EvalError({ throw EvalError({
.hint = hintfmt("string '%1%' cannot refer to other paths", path), .msg = hintfmt("string '%1%' cannot refer to other paths", path),
.errPos = *attr.pos .errPos = *attr.pos
}); });
} else if (attr.name == state.sName) } else if (attr.name == state.sName)
@ -1885,13 +1908,13 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
else else
throw EvalError({ throw EvalError({
.hint = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name), .msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name),
.errPos = *attr.pos .errPos = *attr.pos
}); });
} }
if (path.empty()) if (path.empty())
throw EvalError({ throw EvalError({
.hint = hintfmt("'path' required"), .msg = hintfmt("'path' required"),
.errPos = pos .errPos = pos
}); });
if (name.empty()) if (name.empty())
@ -2006,7 +2029,7 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end()) if (i == args[1]->attrs->end())
throw EvalError({ throw EvalError({
.hint = hintfmt("attribute '%1%' missing", attr), .msg = hintfmt("attribute '%1%' missing", attr),
.errPos = pos .errPos = pos
}); });
// !!! add to stack trace? // !!! add to stack trace?
@ -2068,7 +2091,7 @@ static RegisterPrimOp primop_hasAttr({
static void prim_isAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
mkBool(v, args[0]->type == tAttrs); mkBool(v, args[0]->type() == nAttrs);
} }
static RegisterPrimOp primop_isAttrs({ static RegisterPrimOp primop_isAttrs({
@ -2138,7 +2161,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
Bindings::iterator j = v2.attrs->find(state.sName); Bindings::iterator j = v2.attrs->find(state.sName);
if (j == v2.attrs->end()) if (j == v2.attrs->end())
throw TypeError({ throw TypeError({
.hint = hintfmt("'name' attribute missing in a call to 'listToAttrs'"), .msg = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
.errPos = pos .errPos = pos
}); });
string name = state.forceStringNoCtx(*j->value, pos); string name = state.forceStringNoCtx(*j->value, pos);
@ -2148,7 +2171,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue)); Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
if (j2 == v2.attrs->end()) if (j2 == v2.attrs->end())
throw TypeError({ throw TypeError({
.hint = hintfmt("'value' attribute missing in a call to 'listToAttrs'"), .msg = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
.errPos = pos .errPos = pos
}); });
v.attrs->push_back(Attr(sym, j2->value, j2->pos)); v.attrs->push_back(Attr(sym, j2->value, j2->pos));
@ -2248,13 +2271,13 @@ static RegisterPrimOp primop_catAttrs({
static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
if (args[0]->type == tPrimOpApp || args[0]->type == tPrimOp) { if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) {
state.mkAttrs(v, 0); state.mkAttrs(v, 0);
return; return;
} }
if (args[0]->type != tLambda) if (!args[0]->isLambda())
throw TypeError({ throw TypeError({
.hint = hintfmt("'functionArgs' requires a function"), .msg = hintfmt("'functionArgs' requires a function"),
.errPos = pos .errPos = pos
}); });
@ -2331,7 +2354,7 @@ static RegisterPrimOp primop_mapAttrs({
static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
mkBool(v, args[0]->isList()); mkBool(v, args[0]->type() == nList);
} }
static RegisterPrimOp primop_isList({ static RegisterPrimOp primop_isList({
@ -2348,7 +2371,7 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu
state.forceList(list, pos); state.forceList(list, pos);
if (n < 0 || (unsigned int) n >= list.listSize()) if (n < 0 || (unsigned int) n >= list.listSize())
throw Error({ throw Error({
.hint = hintfmt("list index %1% is out of bounds", n), .msg = hintfmt("list index %1% is out of bounds", n),
.errPos = pos .errPos = pos
}); });
state.forceValue(*list.listElems()[n], pos); state.forceValue(*list.listElems()[n], pos);
@ -2396,7 +2419,7 @@ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
if (args[0]->listSize() == 0) if (args[0]->listSize() == 0)
throw Error({ throw Error({
.hint = hintfmt("'tail' called on an empty list"), .msg = hintfmt("'tail' called on an empty list"),
.errPos = pos .errPos = pos
}); });
@ -2635,7 +2658,7 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
if (len < 0) if (len < 0)
throw EvalError({ throw EvalError({
.hint = hintfmt("cannot create list of size %1%", len), .msg = hintfmt("cannot create list of size %1%", len),
.errPos = pos .errPos = pos
}); });
@ -2683,7 +2706,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
auto comparator = [&](Value * a, Value * b) { auto comparator = [&](Value * a, Value * b) {
/* Optimization: if the comparator is lessThan, bypass /* Optimization: if the comparator is lessThan, bypass
callFunction. */ callFunction. */
if (args[0]->type == tPrimOp && args[0]->primOp->fun == prim_lessThan) if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan)
return CompareValues()(a, b); return CompareValues()(a, b);
Value vTmp1, vTmp2; Value vTmp1, vTmp2;
@ -2825,7 +2848,7 @@ static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value &
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type == tFloat || args[1]->type == tFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
mkFloat(v, state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos)); mkFloat(v, state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos));
else else
mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos)); mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos));
@ -2844,7 +2867,7 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value &
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type == tFloat || args[1]->type == tFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
mkFloat(v, state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos)); mkFloat(v, state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos));
else else
mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos)); mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos));
@ -2863,7 +2886,7 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value &
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type == tFloat || args[1]->type == tFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
mkFloat(v, state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos)); mkFloat(v, state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos));
else else
mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos)); mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos));
@ -2886,11 +2909,11 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
NixFloat f2 = state.forceFloat(*args[1], pos); NixFloat f2 = state.forceFloat(*args[1], pos);
if (f2 == 0) if (f2 == 0)
throw EvalError({ throw EvalError({
.hint = hintfmt("division by zero"), .msg = hintfmt("division by zero"),
.errPos = pos .errPos = pos
}); });
if (args[0]->type == tFloat || args[1]->type == tFloat) { if (args[0]->type() == nFloat || args[1]->type() == nFloat) {
mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos)); mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
} else { } else {
NixInt i1 = state.forceInt(*args[0], pos); NixInt i1 = state.forceInt(*args[0], pos);
@ -2898,7 +2921,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
/* Avoid division overflow as it might raise SIGFPE. */ /* Avoid division overflow as it might raise SIGFPE. */
if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1) if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1)
throw EvalError({ throw EvalError({
.hint = hintfmt("overflow in integer division"), .msg = hintfmt("overflow in integer division"),
.errPos = pos .errPos = pos
}); });
@ -3029,7 +3052,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
if (start < 0) if (start < 0)
throw EvalError({ throw EvalError({
.hint = hintfmt("negative start position in 'substring'"), .msg = hintfmt("negative start position in 'substring'"),
.errPos = pos .errPos = pos
}); });
@ -3080,7 +3103,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
throw Error({ throw Error({
.hint = hintfmt("unknown hash type '%1%'", type), .msg = hintfmt("unknown hash type '%1%'", type),
.errPos = pos .errPos = pos
}); });
@ -3144,12 +3167,12 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
if (e.code() == std::regex_constants::error_space) { if (e.code() == std::regex_constants::error_space) {
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
throw EvalError({ throw EvalError({
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re), .msg = hintfmt("memory limit exceeded by regular expression '%s'", re),
.errPos = pos .errPos = pos
}); });
} else { } else {
throw EvalError({ throw EvalError({
.hint = hintfmt("invalid regular expression '%s'", re), .msg = hintfmt("invalid regular expression '%s'", re),
.errPos = pos .errPos = pos
}); });
} }
@ -3252,12 +3275,12 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value
if (e.code() == std::regex_constants::error_space) { if (e.code() == std::regex_constants::error_space) {
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
throw EvalError({ throw EvalError({
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re), .msg = hintfmt("memory limit exceeded by regular expression '%s'", re),
.errPos = pos .errPos = pos
}); });
} else { } else {
throw EvalError({ throw EvalError({
.hint = hintfmt("invalid regular expression '%s'", re), .msg = hintfmt("invalid regular expression '%s'", re),
.errPos = pos .errPos = pos
}); });
} }
@ -3337,7 +3360,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
if (args[0]->listSize() != args[1]->listSize()) if (args[0]->listSize() != args[1]->listSize())
throw EvalError({ throw EvalError({
.hint = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"), .msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
.errPos = pos .errPos = pos
}); });

View file

@ -147,7 +147,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
for (auto & i : *args[1]->attrs) { for (auto & i : *args[1]->attrs) {
if (!state.store->isStorePath(i.name)) if (!state.store->isStorePath(i.name))
throw EvalError({ throw EvalError({
.hint = hintfmt("Context key '%s' is not a store path", i.name), .msg = hintfmt("Context key '%s' is not a store path", i.name),
.errPos = *i.pos .errPos = *i.pos
}); });
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
@ -164,7 +164,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (state.forceBool(*iter->value, *iter->pos)) { if (state.forceBool(*iter->value, *iter->pos)) {
if (!isDerivation(i.name)) { if (!isDerivation(i.name)) {
throw EvalError({ throw EvalError({
.hint = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name), .msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
.errPos = *i.pos .errPos = *i.pos
}); });
} }
@ -177,7 +177,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
state.forceList(*iter->value, *iter->pos); state.forceList(*iter->value, *iter->pos);
if (iter->value->listSize() && !isDerivation(i.name)) { if (iter->value->listSize() && !isDerivation(i.name)) {
throw EvalError({ throw EvalError({
.hint = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name), .msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
.errPos = *i.pos .errPos = *i.pos
}); });
} }

View file

@ -17,7 +17,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
state.forceValue(*args[0]); state.forceValue(*args[0]);
if (args[0]->type == tAttrs) { if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
@ -38,14 +38,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, *attr.pos);
else else
throw EvalError({ throw EvalError({
.hint = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name), .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
.errPos = *attr.pos .errPos = *attr.pos
}); });
} }
if (url.empty()) if (url.empty())
throw EvalError({ throw EvalError({
.hint = hintfmt("'url' argument required"), .msg = hintfmt("'url' argument required"),
.errPos = pos .errPos = pos
}); });

View file

@ -39,11 +39,12 @@ void emitTreeAttrs(
// Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev // Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
auto emptyHash = Hash(htSHA1); auto emptyHash = Hash(htSHA1);
mkString(*state.allocAttr(v, state.symbols.create("rev")), emptyHash.gitRev()); mkString(*state.allocAttr(v, state.symbols.create("rev")), emptyHash.gitRev());
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), emptyHash.gitRev()); mkString(*state.allocAttr(v, state.symbols.create("shortRev")), emptyHash.gitShortRev());
} }
if (input.getType() == "git") if (input.getType() == "git")
mkBool(*state.allocAttr(v, state.symbols.create("submodules")), maybeGetBoolAttr(input.attrs, "submodules").value_or(false)); mkBool(*state.allocAttr(v, state.symbols.create("submodules")),
fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
if (auto revCount = input.getRevCount()) if (auto revCount = input.getRevCount())
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount); mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
@ -84,26 +85,26 @@ static void fetchTree(
state.forceValue(*args[0]); state.forceValue(*args[0]);
if (args[0]->type == tAttrs) { if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
fetchers::Attrs attrs; fetchers::Attrs attrs;
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
state.forceValue(*attr.value); state.forceValue(*attr.value);
if (attr.value->type == tPath || attr.value->type == tString) if (attr.value->type() == nPath || attr.value->type() == nString)
addURI( addURI(
state, state,
attrs, attrs,
attr.name, attr.name,
state.coerceToString(*attr.pos, *attr.value, context, false, false) state.coerceToString(*attr.pos, *attr.value, context, false, false)
); );
else if (attr.value->type == tString) else if (attr.value->type() == nString)
addURI(state, attrs, attr.name, attr.value->string.s); addURI(state, attrs, attr.name, attr.value->string.s);
else if (attr.value->type == tBool) else if (attr.value->type() == nBool)
attrs.emplace(attr.name, fetchers::Explicit<bool>{attr.value->boolean}); attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
else if (attr.value->type == tInt) else if (attr.value->type() == nInt)
attrs.emplace(attr.name, attr.value->integer); attrs.emplace(attr.name, uint64_t(attr.value->integer));
else else
throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected", throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
attr.name, showType(*attr.value)); attr.name, showType(*attr.value));
@ -114,7 +115,7 @@ static void fetchTree(
if (!attrs.count("type")) if (!attrs.count("type"))
throw Error({ throw Error({
.hint = hintfmt("attribute 'type' is missing in call to 'fetchTree'"), .msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
.errPos = pos .errPos = pos
}); });
@ -152,6 +153,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
fetchTree(state, pos, args, v, std::nullopt); fetchTree(state, pos, args, v, std::nullopt);
} }
// FIXME: document
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree); static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
@ -162,7 +164,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
state.forceValue(*args[0]); state.forceValue(*args[0]);
if (args[0]->type == tAttrs) { if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
@ -176,14 +178,14 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, *attr.pos);
else else
throw EvalError({ throw EvalError({
.hint = hintfmt("unsupported argument '%s' to '%s'", attr.name, who), .msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
.errPos = *attr.pos .errPos = *attr.pos
}); });
} }
if (!url) if (!url)
throw EvalError({ throw EvalError({
.hint = hintfmt("'url' argument required"), .msg = hintfmt("'url' argument required"),
.errPos = pos .errPos = pos
}); });
} else } else
@ -211,7 +213,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
? state.store->queryPathInfo(storePath)->narHash ? state.store->queryPathInfo(storePath)->narHash
: hashFile(htSHA256, path); : hashFile(htSHA256, path);
if (hash != *expectedHash) if (hash != *expectedHash)
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n wanted: %s\n got: %s", throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
*url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true)); *url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
} }
@ -323,6 +325,11 @@ static RegisterPrimOp primop_fetchGit({
A Boolean parameter that specifies whether submodules should be A Boolean parameter that specifies whether submodules should be
checked out. Defaults to `false`. checked out. Defaults to `false`.
- 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).
Here are some examples of how to use `fetchGit`. Here are some examples of how to use `fetchGit`.
- To fetch a private repository over SSH: - To fetch a private repository over SSH:

View file

@ -82,7 +82,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
visit(v, parser(tomlStream).parse()); visit(v, parser(tomlStream).parse());
} catch (std::runtime_error & e) { } catch (std::runtime_error & e) {
throw EvalError({ throw EvalError({
.hint = hintfmt("while parsing a TOML string: %s", e.what()), .msg = hintfmt("while parsing a TOML string: %s", e.what()),
.errPos = pos .errPos = pos
}); });
} }

View file

@ -16,30 +16,30 @@ void printValueAsJSON(EvalState & state, bool strict,
if (strict) state.forceValue(v); if (strict) state.forceValue(v);
switch (v.type) { switch (v.type()) {
case tInt: case nInt:
out.write(v.integer); out.write(v.integer);
break; break;
case tBool: case nBool:
out.write(v.boolean); out.write(v.boolean);
break; break;
case tString: case nString:
copyContext(v, context); copyContext(v, context);
out.write(v.string.s); out.write(v.string.s);
break; break;
case tPath: case nPath:
out.write(state.copyPathToStore(context, v.path)); out.write(state.copyPathToStore(context, v.path));
break; break;
case tNull: case nNull:
out.write(nullptr); out.write(nullptr);
break; break;
case tAttrs: { case nAttrs: {
auto maybeString = state.tryAttrsToString(noPos, v, context, false, false); auto maybeString = state.tryAttrsToString(noPos, v, context, false, false);
if (maybeString) { if (maybeString) {
out.write(*maybeString); out.write(*maybeString);
@ -61,7 +61,7 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
} }
case tList1: case tList2: case tListN: { case nList: {
auto list(out.list()); auto list(out.list());
for (unsigned int n = 0; n < v.listSize(); ++n) { for (unsigned int n = 0; n < v.listSize(); ++n) {
auto placeholder(list.placeholder()); auto placeholder(list.placeholder());
@ -70,15 +70,18 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
} }
case tExternal: case nExternal:
v.external->printValueAsJSON(state, strict, out, context); v.external->printValueAsJSON(state, strict, out, context);
break; break;
case tFloat: case nFloat:
out.write(v.fpoint); out.write(v.fpoint);
break; break;
default: case nThunk:
throw TypeError("cannot convert %1% to JSON", showType(v));
case nFunction:
throw TypeError("cannot convert %1% to JSON", showType(v)); throw TypeError("cannot convert %1% to JSON", showType(v));
} }
} }

View file

@ -58,31 +58,31 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
if (strict) state.forceValue(v); if (strict) state.forceValue(v);
switch (v.type) { switch (v.type()) {
case tInt: case nInt:
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str())); doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str()));
break; break;
case tBool: case nBool:
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false")); doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
break; break;
case tString: case nString:
/* !!! show the context? */ /* !!! show the context? */
copyContext(v, context); copyContext(v, context);
doc.writeEmptyElement("string", singletonAttrs("value", v.string.s)); doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
break; break;
case tPath: case nPath:
doc.writeEmptyElement("path", singletonAttrs("value", v.path)); doc.writeEmptyElement("path", singletonAttrs("value", v.path));
break; break;
case tNull: case nNull:
doc.writeEmptyElement("null"); doc.writeEmptyElement("null");
break; break;
case tAttrs: case nAttrs:
if (state.isDerivation(v)) { if (state.isDerivation(v)) {
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
@ -92,14 +92,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
a = v.attrs->find(state.sDrvPath); a = v.attrs->find(state.sDrvPath);
if (a != v.attrs->end()) { if (a != v.attrs->end()) {
if (strict) state.forceValue(*a->value); if (strict) state.forceValue(*a->value);
if (a->value->type == tString) if (a->value->type() == nString)
xmlAttrs["drvPath"] = drvPath = a->value->string.s; xmlAttrs["drvPath"] = drvPath = a->value->string.s;
} }
a = v.attrs->find(state.sOutPath); a = v.attrs->find(state.sOutPath);
if (a != v.attrs->end()) { if (a != v.attrs->end()) {
if (strict) state.forceValue(*a->value); if (strict) state.forceValue(*a->value);
if (a->value->type == tString) if (a->value->type() == nString)
xmlAttrs["outPath"] = a->value->string.s; xmlAttrs["outPath"] = a->value->string.s;
} }
@ -118,14 +118,19 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
case tList1: case tList2: case tListN: { case nList: {
XMLOpenElement _(doc, "list"); XMLOpenElement _(doc, "list");
for (unsigned int n = 0; n < v.listSize(); ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen); printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
break; break;
} }
case tLambda: { case nFunction: {
if (!v.isLambda()) {
// FIXME: Serialize primops and primopapps
doc.writeEmptyElement("unevaluated");
break;
}
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
if (location) posToXML(xmlAttrs, v.lambda.fun->pos); if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
XMLOpenElement _(doc, "function", xmlAttrs); XMLOpenElement _(doc, "function", xmlAttrs);
@ -143,15 +148,15 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
} }
case tExternal: case nExternal:
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen); v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
break; break;
case tFloat: case nFloat:
doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str())); doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
break; break;
default: case nThunk:
doc.writeEmptyElement("unevaluated"); doc.writeEmptyElement("unevaluated");
} }
} }

View file

@ -27,8 +27,24 @@ typedef enum {
tPrimOpApp, tPrimOpApp,
tExternal, tExternal,
tFloat tFloat
} ValueType; } InternalType;
// This type abstracts over all actual value types in the language,
// grouping together implementation details like tList*, different function
// types, and types in non-normal form (so thunks and co.)
typedef enum {
nThunk,
nInt,
nFloat,
nBool,
nString,
nPath,
nNull,
nAttrs,
nList,
nFunction,
nExternal
} ValueType;
class Bindings; class Bindings;
struct Env; struct Env;
@ -90,7 +106,28 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
struct Value struct Value
{ {
ValueType type; private:
InternalType internalType;
friend std::string showType(const Value & v);
friend void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v);
public:
// Functions needed to distinguish the type
// These should be removed eventually, by putting the functionality that's
// needed by callers into methods of this type
// type() == nThunk
inline bool isThunk() const { return internalType == tThunk; };
inline bool isApp() const { return internalType == tApp; };
inline bool isBlackhole() const { return internalType == tBlackhole; };
// type() == nFunction
inline bool isLambda() const { return internalType == tLambda; };
inline bool isPrimOp() const { return internalType == tPrimOp; };
inline bool isPrimOpApp() const { return internalType == tPrimOpApp; };
union union
{ {
NixInt integer; NixInt integer;
@ -147,24 +184,161 @@ struct Value
NixFloat fpoint; NixFloat fpoint;
}; };
// Returns the normal type of a Value. This only returns nThunk if the
// Value hasn't been forceValue'd
inline ValueType type() const
{
switch (internalType) {
case tInt: return nInt;
case tBool: return nBool;
case tString: return nString;
case tPath: return nPath;
case tNull: return nNull;
case tAttrs: return nAttrs;
case tList1: case tList2: case tListN: return nList;
case tLambda: case tPrimOp: case tPrimOpApp: return nFunction;
case tExternal: return nExternal;
case tFloat: return nFloat;
case tThunk: case tApp: case tBlackhole: return nThunk;
}
abort();
}
/* After overwriting an app node, be sure to clear pointers in the
Value to ensure that the target isn't kept alive unnecessarily. */
inline void clearValue()
{
app.left = app.right = 0;
}
inline void mkInt(NixInt n)
{
clearValue();
internalType = tInt;
integer = n;
}
inline void mkBool(bool b)
{
clearValue();
internalType = tBool;
boolean = b;
}
inline void mkString(const char * s, const char * * context = 0)
{
internalType = tString;
string.s = s;
string.context = context;
}
inline void mkPath(const char * s)
{
clearValue();
internalType = tPath;
path = s;
}
inline void mkNull()
{
clearValue();
internalType = tNull;
}
inline void mkAttrs(Bindings * a)
{
clearValue();
internalType = tAttrs;
attrs = a;
}
inline void mkList(size_t size)
{
clearValue();
if (size == 1)
internalType = tList1;
else if (size == 2)
internalType = tList2;
else {
internalType = tListN;
bigList.size = size;
}
}
inline void mkThunk(Env * e, Expr * ex)
{
internalType = tThunk;
thunk.env = e;
thunk.expr = ex;
}
inline void mkApp(Value * l, Value * r)
{
internalType = tApp;
app.left = l;
app.right = r;
}
inline void mkLambda(Env * e, ExprLambda * f)
{
internalType = tLambda;
lambda.env = e;
lambda.fun = f;
}
inline void mkBlackhole()
{
internalType = tBlackhole;
// Value will be overridden anyways
}
inline void mkPrimOp(PrimOp * p)
{
clearValue();
internalType = tPrimOp;
primOp = p;
}
inline void mkPrimOpApp(Value * l, Value * r)
{
internalType = tPrimOpApp;
app.left = l;
app.right = r;
}
inline void mkExternal(ExternalValueBase * e)
{
clearValue();
internalType = tExternal;
external = e;
}
inline void mkFloat(NixFloat n)
{
clearValue();
internalType = tFloat;
fpoint = n;
}
bool isList() const bool isList() const
{ {
return type == tList1 || type == tList2 || type == tListN; return internalType == tList1 || internalType == tList2 || internalType == tListN;
} }
Value * * listElems() Value * * listElems()
{ {
return type == tList1 || type == tList2 ? smallList : bigList.elems; return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems;
} }
const Value * const * listElems() const const Value * const * listElems() const
{ {
return type == tList1 || type == tList2 ? smallList : bigList.elems; return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems;
} }
size_t listSize() const size_t listSize() const
{ {
return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size; return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
} }
/* Check whether forcing this value requires a trivial amount of /* Check whether forcing this value requires a trivial amount of
@ -176,86 +350,42 @@ struct Value
}; };
/* After overwriting an app node, be sure to clear pointers in the
Value to ensure that the target isn't kept alive unnecessarily. */
static inline void clearValue(Value & v)
{
v.app.left = v.app.right = 0;
}
// TODO: Remove these static functions, replace call sites with v.mk* instead
static inline void mkInt(Value & v, NixInt n) static inline void mkInt(Value & v, NixInt n)
{ {
clearValue(v); v.mkInt(n);
v.type = tInt;
v.integer = n;
} }
static inline void mkFloat(Value & v, NixFloat n) static inline void mkFloat(Value & v, NixFloat n)
{ {
clearValue(v); v.mkFloat(n);
v.type = tFloat;
v.fpoint = n;
} }
static inline void mkBool(Value & v, bool b) static inline void mkBool(Value & v, bool b)
{ {
clearValue(v); v.mkBool(b);
v.type = tBool;
v.boolean = b;
} }
static inline void mkNull(Value & v) static inline void mkNull(Value & v)
{ {
clearValue(v); v.mkNull();
v.type = tNull;
} }
static inline void mkApp(Value & v, Value & left, Value & right) static inline void mkApp(Value & v, Value & left, Value & right)
{ {
v.type = tApp; v.mkApp(&left, &right);
v.app.left = &left;
v.app.right = &right;
} }
static inline void mkPrimOpApp(Value & v, Value & left, Value & right)
{
v.type = tPrimOpApp;
v.app.left = &left;
v.app.right = &right;
}
static inline void mkStringNoCopy(Value & v, const char * s)
{
v.type = tString;
v.string.s = s;
v.string.context = 0;
}
static inline void mkString(Value & v, const Symbol & s) static inline void mkString(Value & v, const Symbol & s)
{ {
mkStringNoCopy(v, ((const string &) s).c_str()); v.mkString(((const string &) s).c_str());
} }
void mkString(Value & v, const char * s); void mkString(Value & v, const char * s);
static inline void mkPathNoCopy(Value & v, const char * s)
{
clearValue(v);
v.type = tPath;
v.path = s;
}
void mkPath(Value & v, const char * s); void mkPath(Value & v, const char * s);

View file

@ -11,11 +11,11 @@ Attrs jsonToAttrs(const nlohmann::json & json)
for (auto & i : json.items()) { for (auto & i : json.items()) {
if (i.value().is_number()) if (i.value().is_number())
attrs.emplace(i.key(), i.value().get<int64_t>()); attrs.emplace(i.key(), i.value().get<uint64_t>());
else if (i.value().is_string()) else if (i.value().is_string())
attrs.emplace(i.key(), i.value().get<std::string>()); attrs.emplace(i.key(), i.value().get<std::string>());
else if (i.value().is_boolean()) else if (i.value().is_boolean())
attrs.emplace(i.key(), i.value().get<bool>()); attrs.emplace(i.key(), Explicit<bool> { i.value().get<bool>() });
else else
throw Error("unsupported input attribute type in lock file"); throw Error("unsupported input attribute type in lock file");
} }
@ -23,7 +23,7 @@ Attrs jsonToAttrs(const nlohmann::json & json)
return attrs; return attrs;
} }
nlohmann::json attrsToJson(const Attrs & attrs) nlohmann::json attrsToJSON(const Attrs & attrs)
{ {
nlohmann::json json; nlohmann::json json;
for (auto & attr : attrs) { for (auto & attr : attrs) {
@ -44,7 +44,7 @@ std::optional<std::string> maybeGetStrAttr(const Attrs & attrs, const std::strin
if (i == attrs.end()) return {}; if (i == attrs.end()) return {};
if (auto v = std::get_if<std::string>(&i->second)) if (auto v = std::get_if<std::string>(&i->second))
return *v; return *v;
throw Error("input attribute '%s' is not a string %s", name, attrsToJson(attrs).dump()); throw Error("input attribute '%s' is not a string %s", name, attrsToJSON(attrs).dump());
} }
std::string getStrAttr(const Attrs & attrs, const std::string & name) std::string getStrAttr(const Attrs & attrs, const std::string & name)

View file

@ -8,24 +8,12 @@
namespace nix::fetchers { namespace nix::fetchers {
/* Wrap bools to prevent string literals (i.e. 'char *') from being
cast to a bool in Attr. */
template<typename T>
struct Explicit {
T t;
bool operator ==(const Explicit<T> & other) const
{
return t == other.t;
}
};
typedef std::variant<std::string, uint64_t, Explicit<bool>> Attr; typedef std::variant<std::string, uint64_t, Explicit<bool>> Attr;
typedef std::map<std::string, Attr> Attrs; typedef std::map<std::string, Attr> Attrs;
Attrs jsonToAttrs(const nlohmann::json & json); Attrs jsonToAttrs(const nlohmann::json & json);
nlohmann::json attrsToJson(const Attrs & attrs); nlohmann::json attrsToJSON(const Attrs & attrs);
std::optional<std::string> maybeGetStrAttr(const Attrs & attrs, const std::string & name); std::optional<std::string> maybeGetStrAttr(const Attrs & attrs, const std::string & name);

View file

@ -55,8 +55,8 @@ struct CacheImpl : Cache
bool immutable) override bool immutable) override
{ {
_state.lock()->add.use() _state.lock()->add.use()
(attrsToJson(inAttrs).dump()) (attrsToJSON(inAttrs).dump())
(attrsToJson(infoAttrs).dump()) (attrsToJSON(infoAttrs).dump())
(store->printStorePath(storePath)) (store->printStorePath(storePath))
(immutable) (immutable)
(time(0)).exec(); (time(0)).exec();
@ -70,7 +70,7 @@ struct CacheImpl : Cache
if (!res->expired) if (!res->expired)
return std::make_pair(std::move(res->infoAttrs), std::move(res->storePath)); return std::make_pair(std::move(res->infoAttrs), std::move(res->storePath));
debug("ignoring expired cache entry '%s'", debug("ignoring expired cache entry '%s'",
attrsToJson(inAttrs).dump()); attrsToJSON(inAttrs).dump());
} }
return {}; return {};
} }
@ -81,15 +81,15 @@ struct CacheImpl : Cache
{ {
auto state(_state.lock()); auto state(_state.lock());
auto inAttrsJson = attrsToJson(inAttrs).dump(); auto inAttrsJSON = attrsToJSON(inAttrs).dump();
auto stmt(state->lookup.use()(inAttrsJson)); auto stmt(state->lookup.use()(inAttrsJSON));
if (!stmt.next()) { if (!stmt.next()) {
debug("did not find cache entry for '%s'", inAttrsJson); debug("did not find cache entry for '%s'", inAttrsJSON);
return {}; return {};
} }
auto infoJson = stmt.getStr(0); auto infoJSON = stmt.getStr(0);
auto storePath = store->parseStorePath(stmt.getStr(1)); auto storePath = store->parseStorePath(stmt.getStr(1));
auto immutable = stmt.getInt(2) != 0; auto immutable = stmt.getInt(2) != 0;
auto timestamp = stmt.getInt(3); auto timestamp = stmt.getInt(3);
@ -97,16 +97,16 @@ struct CacheImpl : Cache
store->addTempRoot(storePath); store->addTempRoot(storePath);
if (!store->isValidPath(storePath)) { if (!store->isValidPath(storePath)) {
// FIXME: we could try to substitute 'storePath'. // FIXME: we could try to substitute 'storePath'.
debug("ignoring disappeared cache entry '%s'", inAttrsJson); debug("ignoring disappeared cache entry '%s'", inAttrsJSON);
return {}; return {};
} }
debug("using cache entry '%s' -> '%s', '%s'", debug("using cache entry '%s' -> '%s', '%s'",
inAttrsJson, infoJson, store->printStorePath(storePath)); inAttrsJSON, infoJSON, store->printStorePath(storePath));
return Result { return Result {
.expired = !immutable && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)), .expired = !immutable && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)),
.infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJson)), .infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)),
.storePath = std::move(storePath) .storePath = std::move(storePath)
}; };
} }

View file

@ -65,7 +65,7 @@ Input Input::fromAttrs(Attrs && attrs)
ParsedURL Input::toURL() const ParsedURL Input::toURL() const
{ {
if (!scheme) if (!scheme)
throw Error("cannot show unsupported input '%s'", attrsToJson(attrs)); throw Error("cannot show unsupported input '%s'", attrsToJSON(attrs));
return scheme->toURL(*this); return scheme->toURL(*this);
} }
@ -110,7 +110,7 @@ bool Input::contains(const Input & other) const
std::pair<Tree, Input> Input::fetch(ref<Store> store) const std::pair<Tree, Input> Input::fetch(ref<Store> store) const
{ {
if (!scheme) if (!scheme)
throw Error("cannot fetch unsupported input '%s'", attrsToJson(toAttrs())); throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
/* The tree may already be in the Nix store, or it could be /* The tree may already be in the Nix store, or it could be
substituted (which is often faster than fetching from the substituted (which is often faster than fetching from the
@ -132,7 +132,14 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
} }
} }
auto [tree, input] = scheme->fetch(store, *this); auto [tree, input] = [&]() -> std::pair<Tree, Input> {
try {
return scheme->fetch(store, *this);
} catch (Error & e) {
e.addTrace({}, "while fetching the input '%s'", to_string());
throw;
}
}();
if (tree.actualPath == "") if (tree.actualPath == "")
tree.actualPath = store->toRealPath(tree.storePath); tree.actualPath = store->toRealPath(tree.storePath);
@ -253,7 +260,7 @@ std::optional<time_t> Input::getLastModified() const
ParsedURL InputScheme::toURL(const Input & input) ParsedURL InputScheme::toURL(const Input & input)
{ {
throw Error("don't know how to convert input '%s' to a URL", attrsToJson(input.attrs)); throw Error("don't know how to convert input '%s' to a URL", attrsToJSON(input.attrs));
} }
Input InputScheme::applyOverrides( Input InputScheme::applyOverrides(

View file

@ -21,6 +21,14 @@ struct Tree
struct InputScheme; struct InputScheme;
/* The Input object is generated by a specific fetcher, based on the
* user-supplied input attribute in the flake.nix file, and contains
* the information that the specific fetcher needs to perform the
* actual fetch. The Input object is most commonly created via the
* "fromURL()" or "fromAttrs()" static functions which are provided
* the url or attrset specified in the flake file.
*/
struct Input struct Input
{ {
friend struct InputScheme; friend struct InputScheme;
@ -84,6 +92,16 @@ public:
std::optional<time_t> getLastModified() const; std::optional<time_t> getLastModified() const;
}; };
/* The InputScheme represents a type of fetcher. Each fetcher
* registers with nix at startup time. When processing an input for a
* flake, each scheme is given an opportunity to "recognize" that
* input from the url or attributes in the flake file's specification
* and return an Input object to represent the input if it is
* recognized. The Input object contains the information the fetcher
* needs to actually perform the "fetch()" when called.
*/
struct InputScheme struct InputScheme
{ {
virtual ~InputScheme() virtual ~InputScheme()

View file

@ -59,12 +59,13 @@ struct GitInputScheme : InputScheme
if (maybeGetStrAttr(attrs, "type") != "git") return {}; if (maybeGetStrAttr(attrs, "type") != "git") return {};
for (auto & [name, value] : attrs) for (auto & [name, value] : attrs)
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash") if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash" && name != "allRefs")
throw Error("unsupported Git input attribute '%s'", name); throw Error("unsupported Git input attribute '%s'", name);
parseURL(getStrAttr(attrs, "url")); parseURL(getStrAttr(attrs, "url"));
maybeGetBoolAttr(attrs, "shallow"); maybeGetBoolAttr(attrs, "shallow");
maybeGetBoolAttr(attrs, "submodules"); maybeGetBoolAttr(attrs, "submodules");
maybeGetBoolAttr(attrs, "allRefs");
if (auto ref = maybeGetStrAttr(attrs, "ref")) { if (auto ref = maybeGetStrAttr(attrs, "ref")) {
if (std::regex_search(*ref, badGitRefRegex)) if (std::regex_search(*ref, badGitRefRegex))
@ -169,10 +170,12 @@ struct GitInputScheme : InputScheme
bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false); bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false);
bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false); bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
bool allRefs = maybeGetBoolAttr(input.attrs, "allRefs").value_or(false);
std::string cacheType = "git"; std::string cacheType = "git";
if (shallow) cacheType += "-shallow"; if (shallow) cacheType += "-shallow";
if (submodules) cacheType += "-submodules"; if (submodules) cacheType += "-submodules";
if (allRefs) cacheType += "-all-refs";
auto getImmutableAttrs = [&]() auto getImmutableAttrs = [&]()
{ {
@ -273,7 +276,7 @@ struct GitInputScheme : InputScheme
haveCommits ? std::stoull(runProgram("git", true, { "-C", actualUrl, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0); haveCommits ? std::stoull(runProgram("git", true, { "-C", actualUrl, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
return { return {
Tree(store->printStorePath(storePath), std::move(storePath)), Tree(store->toRealPath(storePath), std::move(storePath)),
input input
}; };
} }
@ -338,11 +341,15 @@ struct GitInputScheme : InputScheme
} }
} }
} else { } else {
/* If the local ref is older than tarball-ttl seconds, do a if (allRefs) {
git fetch to update the local ref to the remote ref. */ doFetch = true;
struct stat st; } else {
doFetch = stat(localRefFile.c_str(), &st) != 0 || /* If the local ref is older than tarball-ttl seconds, do a
(uint64_t) st.st_mtime + settings.tarballTtl <= (uint64_t) now; git fetch to update the local ref to the remote ref. */
struct stat st;
doFetch = stat(localRefFile.c_str(), &st) != 0 ||
(uint64_t) st.st_mtime + settings.tarballTtl <= (uint64_t) now;
}
} }
if (doFetch) { if (doFetch) {
@ -352,9 +359,11 @@ struct GitInputScheme : InputScheme
// we're using --quiet for now. Should process its stderr. // we're using --quiet for now. Should process its stderr.
try { try {
auto ref = input.getRef(); auto ref = input.getRef();
auto fetchRef = ref->compare(0, 5, "refs/") == 0 auto fetchRef = allRefs
? *ref ? "refs/*"
: "refs/heads/" + *ref; : ref->compare(0, 5, "refs/") == 0
? *ref
: "refs/heads/" + *ref;
runProgram("git", true, { "-C", repoDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", fetchRef, fetchRef) }); runProgram("git", true, { "-C", repoDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", fetchRef, fetchRef) });
} catch (Error & e) { } catch (Error & e) {
if (!pathExists(localRefFile)) throw; if (!pathExists(localRefFile)) throw;
@ -392,6 +401,28 @@ struct GitInputScheme : InputScheme
AutoDelete delTmpDir(tmpDir, true); AutoDelete delTmpDir(tmpDir, true);
PathFilter filter = defaultPathFilter; PathFilter filter = defaultPathFilter;
RunOptions checkCommitOpts(
"git",
{ "-C", repoDir, "cat-file", "commit", input.getRev()->gitRev() }
);
checkCommitOpts.searchPath = true;
checkCommitOpts.mergeStderrToStdout = true;
auto result = runProgram(checkCommitOpts);
if (WEXITSTATUS(result.first) == 128
&& result.second.find("bad file") != std::string::npos
) {
throw Error(
"Cannot find Git revision '%s' in ref '%s' of repository '%s'! "
"Please make sure that the " ANSI_BOLD "rev" ANSI_NORMAL " exists on the "
ANSI_BOLD "ref" ANSI_NORMAL " you've specified or add " ANSI_BOLD
"allRefs = true;" ANSI_NORMAL " to " ANSI_BOLD "fetchGit" ANSI_NORMAL ".",
input.getRev()->gitRev(),
*input.getRef(),
actualUrl
);
}
if (submodules) { if (submodules) {
Path tmpGitDir = createTempDir(); Path tmpGitDir = createTempDir();
AutoDelete delTmpGitDir(tmpGitDir, true); AutoDelete delTmpGitDir(tmpGitDir, true);

View file

@ -37,15 +37,29 @@ struct GitArchiveInputScheme : InputScheme
std::optional<std::string> ref; std::optional<std::string> ref;
std::optional<std::string> host_url; std::optional<std::string> host_url;
if (path.size() == 2) { auto size = path.size();
} else if (path.size() == 3) { if (size == 3) {
if (std::regex_match(path[2], revRegex)) if (std::regex_match(path[2], revRegex))
rev = Hash::parseAny(path[2], htSHA1); rev = Hash::parseAny(path[2], htSHA1);
else if (std::regex_match(path[2], refRegex)) else if (std::regex_match(path[2], refRegex))
ref = path[2]; ref = path[2];
else else
throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]); throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]);
} else } else if (size > 3) {
std::string rs;
for (auto i = std::next(path.begin(), 2); i != path.end(); i++) {
rs += *i;
if (std::next(i) != path.end()) {
rs += "/";
}
}
if (std::regex_match(rs, refRegex)) {
ref = rs;
} else {
throw BadURL("in URL '%s', '%s' is not a branch/tag name", url.url, rs);
}
} else if (size < 2)
throw BadURL("URL '%s' is invalid", url.url); throw BadURL("URL '%s' is invalid", url.url);
for (auto &[name, value] : url.query) { for (auto &[name, value] : url.query) {
@ -195,14 +209,14 @@ struct GitArchiveInputScheme : InputScheme
auto [tree, lastModified] = 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", lastModified); input.attrs.insert_or_assign("lastModified", uint64_t(lastModified));
getCache()->add( getCache()->add(
store, store,
immutableAttrs, immutableAttrs,
{ {
{"rev", rev->gitRev()}, {"rev", rev->gitRev()},
{"lastModified", lastModified} {"lastModified", uint64_t(lastModified)}
}, },
tree.storePath, tree.storePath,
true); true);

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