diff options
43 files changed, 1411 insertions, 886 deletions
diff --git a/configure.ac b/configure.ac index 7177c8914434..c41a83c97646 100644 --- a/configure.ac +++ b/configure.ac @@ -215,7 +215,7 @@ AC_SUBST(ENABLE_S3, [$enable_s3]) AC_LANG_POP(C++) if test -n "$enable_s3"; then - declare -a aws_version_tokens=($(printf '#include <aws/core/VersionConfig.h>\nAWS_SDK_VERSION_STRING' | $CPP - | 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_MINOR], ${aws_version_tokens@<:@1@:>@}, [Minor version of aws-sdk-cpp.]) fi diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml index 6638bf61e454..431c0e6d3570 100644 --- a/doc/manual/command-ref/conf-file.xml +++ b/doc/manual/command-ref/conf-file.xml @@ -456,6 +456,36 @@ builtins.fetchurl { </varlistentry> + <varlistentry xml:id="conf-narinfo-cache-negative-ttl"><term><literal>narinfo-cache-negative-ttl</literal></term> + + <listitem> + + <para>The TTL in seconds for negative lookups. If a store path is + queried from a substituter but was not found, there will be a + negative lookup cached in the local disk cache database for the + specified duration.</para> + + </listitem> + + </varlistentry> + + <varlistentry xml:id="conf-narinfo-cache-positive-ttl"><term><literal>narinfo-cache-positive-ttl</literal></term> + + <listitem> + + <para>The TTL in seconds for positive lookups. If a store path is + queried from a substituter, the result of the query will be cached + in the local disk cache database including some of the NAR + metadata. The default TTL is a month, setting a shorter TTL for + positive lookups can be useful for binary caches that have + frequent garbage collection, in which case having a more frequent + cache invalidation would prevent trying to pull the path again and + failing with a hash mismatch if the build isn't reproducible. + </para> + + </listitem> + + </varlistentry> <varlistentry xml:id="conf-netrc-file"><term><literal>netrc-file</literal></term> @@ -511,7 +541,6 @@ password <replaceable>my-password</replaceable> </varlistentry> - <varlistentry xml:id="conf-pre-build-hook"><term><literal>pre-build-hook</literal></term> <listitem> @@ -788,7 +817,6 @@ password <replaceable>my-password</replaceable> </varlistentry> - </variablelist> </para> diff --git a/doc/manual/command-ref/nix-env.xml b/doc/manual/command-ref/nix-env.xml index d4563ac47551..eac7739558be 100644 --- a/doc/manual/command-ref/nix-env.xml +++ b/doc/manual/command-ref/nix-env.xml @@ -456,7 +456,7 @@ $ nix-env -f ~/foo.nix -i '.*'</screen> from another profile: <screen> -$ nix-env -i --from-profile /nix/var/nix/profiles/foo -i gcc</screen> +$ nix-env -i --from-profile /nix/var/nix/profiles/foo gcc</screen> </para> diff --git a/doc/manual/command-ref/opt-common-syn.xml b/doc/manual/command-ref/opt-common-syn.xml index 168bef080f4f..b610b54b9620 100644 --- a/doc/manual/command-ref/opt-common-syn.xml +++ b/doc/manual/command-ref/opt-common-syn.xml @@ -9,6 +9,9 @@ </group> </arg> <arg> + <arg choice='plain'><option>--quiet</option></arg> +</arg> +<arg> <group choice='plain'> <arg choice='plain'><option>--no-build-output</option></arg> <arg choice='plain'><option>-Q</option></arg> diff --git a/doc/manual/command-ref/opt-common.xml b/doc/manual/command-ref/opt-common.xml index bcb60b30125c..4c572e129445 100644 --- a/doc/manual/command-ref/opt-common.xml +++ b/doc/manual/command-ref/opt-common.xml @@ -75,6 +75,23 @@ </varlistentry> +<varlistentry><term><option>--quiet</option></term> + + <listitem> + + <para>Decreases the level of verbosity of diagnostic messages + printed on standard error. This is the inverse option to + <option>-v</option> / <option>--verbose</option>. + </para> + + <para>This option may be specified repeatedly. See the previous + verbosity levels list.</para> + + </listitem> + +</varlistentry> + + <varlistentry><term><option>--no-build-output</option> / <option>-Q</option></term> <listitem><para>By default, output written by builders to standard diff --git a/doc/manual/expressions/builtins.xml b/doc/manual/expressions/builtins.xml index 47b98460adf6..ac1fe7e2fafe 100644 --- a/doc/manual/expressions/builtins.xml +++ b/doc/manual/expressions/builtins.xml @@ -1203,7 +1203,10 @@ in foo</programlisting> This is not allowed because it would cause a cyclic dependency in the computation of the cryptographic hashes for - <varname>foo</varname> and <varname>bar</varname>.</para></listitem> + <varname>foo</varname> and <varname>bar</varname>.</para> + <para>It is also not possible to reference the result of a derivation. + If you are using Nixpkgs, the <literal>writeTextFile</literal> function is able to + do that.</para></listitem> </varlistentry> diff --git a/doc/manual/glossary/glossary.xml b/doc/manual/glossary/glossary.xml index e0636044cc25..4977825578f1 100644 --- a/doc/manual/glossary/glossary.xml +++ b/doc/manual/glossary/glossary.xml @@ -85,29 +85,48 @@ <glossentry xml:id="gloss-reference"><glossterm>reference</glossterm> - <glossdef><para>A store path <varname>P</varname> is said to have a - reference to a store path <varname>Q</varname> if the store object - at <varname>P</varname> contains the path <varname>Q</varname> - somewhere. This implies than an execution involving - <varname>P</varname> potentially needs <varname>Q</varname> to be - present. The <emphasis>references</emphasis> of a store path are - the set of store paths to which it has a reference.</para></glossdef> + <glossdef> + <para>A store path <varname>P</varname> is said to have a + reference to a store path <varname>Q</varname> if the store object + at <varname>P</varname> contains the path <varname>Q</varname> + somewhere. The <emphasis>references</emphasis> of a store path are + the set of store paths to which it has a reference. + </para> + <para>A derivation can reference other derivations and sources + (but not output paths), whereas an output path only references other + output paths. + </para> + </glossdef> </glossentry> +<glossentry xml:id="gloss-reachable"><glossterm>reachable</glossterm> + + <glossdef><para>A store path <varname>Q</varname> is reachable from + another store path <varname>P</varname> if <varname>Q</varname> is in the + <link linkend="gloss-closure">closure</link> of the + <link linkend="gloss-reference">references</link> relation. + </para></glossdef> +</glossentry> <glossentry xml:id="gloss-closure"><glossterm>closure</glossterm> <glossdef><para>The closure of a store path is the set of store paths that are directly or indirectly “reachable” from that store path; that is, it’s the closure of the path under the <link - linkend="gloss-reference">references</link> relation. For instance, - if the store object at path <varname>P</varname> contains a - reference to path <varname>Q</varname>, then <varname>Q</varname> is - in the closure of <varname>P</varname>. For correct deployment it - is necessary to deploy whole closures, since otherwise at runtime - files could be missing. The command <command>nix-store - -qR</command> prints out closures of store paths.</para></glossdef> + linkend="gloss-reference">references</link> relation. For a package, the + closure of its derivation is equivalent to the build-time + dependencies, while the closure of its output path is equivalent to its + runtime dependencies. For correct deployment it is necessary to deploy whole + closures, since otherwise at runtime files could be missing. The command + <command>nix-store -qR</command> prints out closures of store paths. + </para> + <para>As an example, if the store object at path <varname>P</varname> contains + a reference to path <varname>Q</varname>, then <varname>Q</varname> is + in the closure of <varname>P</varname>. Further, if <varname>Q</varname> + references <varname>R</varname> then <varname>R</varname> is also in + the closure of <varname>P</varname>. + </para></glossdef> </glossentry> @@ -147,7 +166,7 @@ linkend="sec-profiles" />.</para> </glossdef> - + </glossentry> diff --git a/doc/manual/installation/prerequisites-source.xml b/doc/manual/installation/prerequisites-source.xml index 49660c36e397..01e9688d635f 100644 --- a/doc/manual/installation/prerequisites-source.xml +++ b/doc/manual/installation/prerequisites-source.xml @@ -9,6 +9,9 @@ <itemizedlist> <listitem><para>GNU Make.</para></listitem> + + <listitem><para>Bash Shell. The <literal>./configure</literal> script + relies on bashisms, so Bash is required.</para></listitem> <listitem><para>A version of GCC or Clang that supports C++14.</para></listitem> @@ -28,6 +31,14 @@ distribution does not provide these, you can obtain bzip2 from <link xlink:href="http://www.bzip.org/"/>.</para></listitem> + <listitem><para><literal>liblzma</literal>, which is provided by + XZ Utils. If your distribution does not provide this, you can + get it from <link xlink:href="https://tukaani.org/xz/"/>.</para></listitem> + + <listitem><para>cURL and its library. If your distribution does not + provide it, you can get it from <link + xlink:href="https://curl.haxx.se/"/>.</para></listitem> + <listitem><para>The SQLite embedded database library, version 3.6.19 or higher. If your distribution does not provide it, please install it from <link xlink:href="http://www.sqlite.org/" />.</para></listitem> diff --git a/doc/manual/introduction/about-nix.xml b/doc/manual/introduction/about-nix.xml index 83a2b6786ac0..e8c0a29753a1 100644 --- a/doc/manual/introduction/about-nix.xml +++ b/doc/manual/introduction/about-nix.xml @@ -60,7 +60,8 @@ This is because tools such as compilers don’t search in per-packages directories such as <filename>/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include</filename>, so if a package builds correctly on your system, this is because you -specified the dependency explicitly.</para> +specified the dependency explicitly. This takes care of the build-time +dependencies.</para> <para>Once a package is built, runtime dependencies are found by scanning binaries for the hash parts of Nix store paths (such as diff --git a/doc/manual/packages/garbage-collection.xml b/doc/manual/packages/garbage-collection.xml index 03b8e4c976c1..a1b0ef22a11e 100644 --- a/doc/manual/packages/garbage-collection.xml +++ b/doc/manual/packages/garbage-collection.xml @@ -52,6 +52,14 @@ garbage collector as follows: <screen> $ nix-store --gc</screen> +The behaviour of the gargage collector is affected by the <literal>keep- +derivations</literal> (default: true) and <literal>keep-outputs</literal> +(default: false) options in the Nix configuration file. The defaults will ensure +that all derivations that are not build-time dependencies of garbage collector roots +will be collected but that all output paths that are not runtime dependencies +will be collected. (This is usually what you want, but while you are developing +it may make sense to keep outputs to ensure that rebuild times are quick.) + If you are feeling uncertain, you can also first view what files would be deleted: diff --git a/mk/programs.mk b/mk/programs.mk index 3ac64494e3a5..2fbda12bd153 100644 --- a/mk/programs.mk +++ b/mk/programs.mk @@ -51,7 +51,7 @@ define build-program else $(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_PATH) | $(DESTDIR)$$($(1)_INSTALL_DIR)/ - install -t $$($(1)_INSTALL_DIR) $$< + install -t $(DESTDIR)$$($(1)_INSTALL_DIR) $$< endif diff --git a/release.nix b/release.nix index 91c782c51916..37deb8e7ee38 100644 --- a/release.nix +++ b/release.nix @@ -28,8 +28,8 @@ let configureFlags = "--enable-gc"; postUnpack = '' - (cd source && find . -type f) | cut -c3- > source/.dist-files - cat source/.dist-files + (cd $sourceRoot && find . -type f) | cut -c3- > $sourceRoot/.dist-files + cat $sourceRoot/.dist-files ''; preConfigure = '' @@ -127,17 +127,39 @@ let substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \ --subst-var-by nix ${toplevel} \ --subst-var-by cacert ${cacert} - substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user \ + + substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \ + --subst-var-by nix ${toplevel} \ + --subst-var-by cacert ${cacert} + substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \ + --subst-var-by nix ${toplevel} \ + --subst-var-by cacert ${cacert} + substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \ --subst-var-by nix ${toplevel} \ --subst-var-by cacert ${cacert} if type -p shellcheck; then - shellcheck -e SC1090 $TMPDIR/install - shellcheck -e SC1091,SC2002 $TMPDIR/install-darwin-multi-user + # SC1090: Don't worry about not being able to find + # $nix/etc/profile.d/nix.sh + shellcheck --exclude SC1090 $TMPDIR/install + shellcheck $TMPDIR/install-darwin-multi-user.sh + shellcheck $TMPDIR/install-systemd-multi-user.sh + + # SC1091: Don't panic about not being able to source + # /etc/profile + # SC2002: Ignore "useless cat" "error", when loading + # .reginfo, as the cat is a much cleaner + # implementation, even though it is "useless" + # SC2116: Allow ROOT_HOME=$(echo ~root) for resolving + # root's home directory + shellcheck --external-sources \ + --exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user fi chmod +x $TMPDIR/install - chmod +x $TMPDIR/install-darwin-multi-user + chmod +x $TMPDIR/install-darwin-multi-user.sh + chmod +x $TMPDIR/install-systemd-multi-user.sh + chmod +x $TMPDIR/install-multi-user dir=nix-${version}-${system} fn=$out/$dir.tar.bz2 mkdir -p $out/nix-support @@ -149,7 +171,9 @@ let --transform "s,$TMPDIR/install,$dir/install," \ --transform "s,$TMPDIR/reginfo,$dir/.reginfo," \ --transform "s,$NIX_STORE,$dir/store,S" \ - $TMPDIR/install $TMPDIR/install-darwin-multi-user $TMPDIR/reginfo \ + $TMPDIR/install $TMPDIR/install-darwin-multi-user.sh \ + $TMPDIR/install-systemd-multi-user.sh \ + $TMPDIR/install-multi-user $TMPDIR/reginfo \ $(cat ${installerClosureInfo}/store-paths) ''); @@ -182,7 +206,6 @@ let }; - rpm_fedora27i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora27i386) [ ]; rpm_fedora27x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora27x86_64) [ ]; @@ -245,7 +268,8 @@ let export NIX_STATE_DIR=$TMPDIR nix-store --init - nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run + nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run \ + --arg nixpkgs '{ outPath = ${nixpkgs}; revCount = 123; shortRev = "abcdefgh"; }' touch $out ''; diff --git a/scripts/install-darwin-multi-user.sh b/scripts/install-darwin-multi-user.sh index 716b6e9bc9a3..87c4c2b0582a 100644 --- a/scripts/install-darwin-multi-user.sh +++ b/scripts/install-darwin-multi-user.sh @@ -1,819 +1,144 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu set -o pipefail -# Sourced from: -# - https://github.com/LnL7/nix-darwin/blob/8c29d0985d74b4a990238497c47a2542a5616b3c/bootstrap.sh -# - https://gist.github.com/expipiplus1/e571ce88c608a1e83547c918591b149f/ac504c6c1b96e65505fbda437a28ce563408ecb0 -# - https://github.com/NixOS/nixos-org-configurations/blob/a122f418797713d519aadf02e677fce0dc1cb446/delft/scripts/nix-mac-installer.sh -# - https://github.com/matthewbauer/macNixOS/blob/f6045394f9153edea417be90c216788e754feaba/install-macNixOS.sh -# - https://gist.github.com/LnL7/9717bd6cdcb30b086fd7f2093e5f8494/86b26f852ce563e973acd30f796a9a416248c34a -# -# however tracking which bits came from which would be impossible. - -readonly ESC='\033[0m' -readonly BOLD='\033[38;1m' -readonly BLUE='\033[38;34m' -readonly BLUE_UL='\033[38;4;34m' -readonly GREEN='\033[38;32m' -readonly GREEN_UL='\033[38;4;32m' -readonly RED='\033[38;31m' -readonly RED_UL='\033[38;4;31m' -readonly YELLOW='\033[38;33m' -readonly YELLOW_UL='\033[38;4;33m' - -readonly CORES=$(sysctl -n hw.ncpu) -readonly NIX_USER_COUNT="32" -readonly NIX_BUILD_GROUP_ID="30000" -readonly NIX_BUILD_GROUP_NAME="nixbld" -readonly NIX_FIRST_BUILD_UID="30001" -# Please don't change this. We don't support it, because the -# default shell profile that comes with Nix doesn't support it. -readonly NIX_ROOT="/nix" readonly PLIST_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist -readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/zshrc") -readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix" -readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" - -readonly NIX_INSTALLED_NIX="@nix@" -readonly NIX_INSTALLED_CACERT="@cacert@" -readonly EXTRACTED_NIX_PATH="$(dirname "$0")" - -readonly ROOT_HOME="/var/root" - -if [ -t 0 ]; then - readonly IS_HEADLESS='no' -else - readonly IS_HEADLESS='yes' -fi +dsclattr() { + /usr/bin/dscl . -read "$1" \ + | awk "/$2/ { print \$2 }" +} -headless() { - if [ "$IS_HEADLESS" = "yes" ]; then - return 0 - else - return 1 +poly_validate_assumptions() { + if [ "$(uname -s)" != "Darwin" ]; then + failure "This script is for use with macOS!" fi } -contactme() { - echo "We'd love to help if you need it." - echo "" - echo "If you can, open an issue at https://github.com/nixos/nix/issues" - echo "" - echo "Or feel free to contact the team," - echo " - on IRC #nixos on irc.freenode.net" - echo " - on twitter @nixos_org" +poly_service_installed_check() { + [ -e "$PLIST_DEST" ] } -uninstall_directions() { - subheader "Uninstalling nix:" - local step=0 - - if [ -e "$PLIST_DEST" ]; then - step=$((step + 1)) +poly_service_uninstall_directions() { cat <<EOF -$step. Delete $PLIST_DEST +$1. Delete $PLIST_DEST sudo launchctl unload $PLIST_DEST sudo rm $PLIST_DEST EOF - fi - - for profile_target in "${PROFILE_TARGETS[@]}"; do - if [ -e "$profile_target" ] && [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then - step=$((step + 1)) - cat <<EOF -$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target - - sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target - -(after this one, you may need to re-open any terminals that were -opened while it existed.) - -EOF - fi - done +} - step=$((step + 1)) +poly_service_setup_note() { cat <<EOF -$step. Delete the files Nix added to your system: - - sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels - -and that is it. + - load and start a LaunchDaemon (at $PLIST_DEST) for nix-daemon EOF - -} - -nix_user_for_core() { - printf "nixbld%d" "$1" -} - -nix_uid_for_core() { - echo $((NIX_FIRST_BUILD_UID + $1 - 1)) } -dsclattr() { - /usr/bin/dscl . -read "$1" \ - | awk "/$2/ { print \$2 }" -} - -_textout() { - echo -en "$1" - shift - if [ "$*" = "" ]; then - cat - else - echo "$@" - fi - echo -en "$ESC" -} - -header() { - follow="---------------------------------------------------------" - header=$(echo "---- $* $follow$follow$follow" | head -c 80) - echo "" - _textout "$BLUE" "$header" -} - -warningheader() { - follow="---------------------------------------------------------" - header=$(echo "---- $* $follow$follow$follow" | head -c 80) - echo "" - _textout "$RED" "$header" -} - -subheader() { - echo "" - _textout "$BLUE_UL" "$*" -} - -row() { - printf "$BOLD%s$ESC:\\t%s\\n" "$1" "$2" -} - -task() { - echo "" - ok "~~> $1" -} - -bold() { - echo "$BOLD$*$ESC" -} - -ok() { - _textout "$GREEN" "$@" -} - -warning() { - warningheader "warning!" - cat - echo "" -} - -failure() { - header "oh no!" - _textout "$RED" "$@" - echo "" - _textout "$RED" "$(contactme)" - trap finish_cleanup EXIT - exit 1 -} - -ui_confirm() { - _textout "$GREEN$GREEN_UL" "$1" - - if headless; then - echo "No TTY, assuming you would say yes :)" - return 0 - fi - - local prompt="[y/n] " - echo -n "$prompt" - while read -r y; do - if [ "$y" = "y" ]; then - echo "" - return 0 - elif [ "$y" = "n" ]; then - echo "" - return 1 - else - _textout "$RED" "Sorry, I didn't understand. I can only understand answers of y or n" - echo -n "$prompt" - fi - done - echo "" - return 1 -} - -__sudo() { - local expl="$1" - local cmd="$2" - shift - header "sudo execution" - - echo "I am executing:" - echo "" - printf " $ sudo %s\\n" "$cmd" - echo "" - echo "$expl" - echo "" - - return 0 -} +poly_configure_nix_daemon_service() { + _sudo "to set up the nix-daemon as a LaunchDaemon" \ + ln -sfn "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST" -_sudo() { - local expl="$1" - shift - if ! headless; then - __sudo "$expl" "$*" - fi - sudo "$@" -} + _sudo "to load the LaunchDaemon plist for nix-daemon" \ + launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist + _sudo "to start the nix-daemon" \ + launchctl start org.nixos.nix-daemon -readonly SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX) -function finish_cleanup { - rm -rf "$SCRATCH" } -function finish_fail { - finish_cleanup - - failure <<EOF -Jeeze, something went wrong. If you can take all the output and open -an issue, we'd love to fix the problem so nobody else has this issue. - -:( -EOF +poly_group_exists() { + /usr/bin/dscl . -read "/Groups/$1" > /dev/null 2>&1 } -trap finish_fail EXIT - -function finish_success { - finish_cleanup - ok "Alright! We're done!" - 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: - - $ nix-shell -p nix-info --run "nix-info -m" - -Thank you for using this installer. If you have any feedback, don't -hesitate: - -$(contactme) -EOF +poly_group_id_get() { + dsclattr "/Groups/$1" "PrimaryGroupID" } - -validate_starting_assumptions() { - if [ "$(uname -s)" != "Darwin" ]; then - failure "This script is for use with macOS!" - fi - - if [ $EUID -eq 0 ]; then - failure <<EOF -Please do not run this script with root privileges. We will call sudo -when we need to. -EOF - fi - - if type nix-env 2> /dev/null >&2; then - failure <<EOF -Nix already appears to be installed, and this tool assumes it is -_not_ yet installed. - -$(uninstall_directions) -EOF - fi - - if [ "${NIX_REMOTE:-}" != "" ]; then - failure <<EOF -For some reason, \$NIX_REMOTE is set. It really should not be set -before this installer runs, and it hints that Nix is currently -installed. Please delete the old Nix installation and start again. - -Note: You might need to close your shell window and open a new shell -to clear the variable. -EOF - fi - - if echo "${SSL_CERT_FILE:-}" | grep -qE "(nix/var/nix|nix-profile)"; then - failure <<EOF -It looks like \$SSL_CERT_FILE is set to a path that used to be part of -the old Nix installation. Please unset that variable and try again: - - $ unset SSL_CERT_FILE - -EOF - fi - - for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do - if [ -f "$file" ]; then - if grep -l "^[^#].*.nix-profile" "$file"; then - failure <<EOF -I found a reference to a ".nix-profile" in $file. -This has a high chance of breaking a new nix installation. It was most -likely put there by a previous Nix installer. - -Please remove this reference and try running this again. You should -also look for similar references in: - - - ~/.bash_profile - - ~/.bash_login - - ~/.profile - -or other shell init files that you may have. - -$(uninstall_directions) -EOF - fi - fi - done - - if [ -d /nix ]; then - failure <<EOF -There are some relics of a previous installation of Nix at /nix, and -this scripts assumes Nix is _not_ yet installed. Please delete the old -Nix installation and start again. - -$(uninstall_directions) -EOF - fi - - if [ -d /etc/nix ]; then - failure <<EOF -There are some relics of a previous installation of Nix at /etc/nix, and -this scripts assumes Nix is _not_ yet installed. Please delete the old -Nix installation and start again. - -$(uninstall_directions) -EOF - fi - - for profile_target in "${PROFILE_TARGETS[@]}"; do - if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then - failure <<EOF -When this script runs, it backs up the current $profile_target to -$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though. - -Please follow these instructions to clean up the old backup file: - -1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just -in case. - -2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like -it has anything nix-related in it. If it does, something is probably -quite wrong. Please open an issue or get in touch immediately. - -3. Take care to make sure that $profile_target doesn't look like it has -anything nix-related in it. If it does, and $profile_target _did not_, -run: - - $ /usr/bin/sudo /bin/mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target - -and try again. -EOF - fi - - if grep -qi "nix" "$profile_target"; then - failure <<EOF -It looks like $profile_target already has some Nix configuration in -there. There should be no reason to run this again. If you're having -trouble, please open an issue. -EOF - fi - done - - danger_paths=("$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.nix-profile") - for danger_path in "${danger_paths[@]}"; do - if _sudo "making sure that $danger_path doesn't exist" \ - test -e "$danger_path"; then - failure <<EOF -I found a file at $danger_path, which is a relic of a previous -installation. You must first delete this file before continuing. - -$(uninstall_directions) -EOF - fi - done +poly_create_build_group() { + _sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \ + /usr/sbin/dseditgroup -o create \ + -r "Nix build group for nix-daemon" \ + -i "$NIX_BUILD_GROUP_ID" \ + "$NIX_BUILD_GROUP_NAME" >&2 } -setup_report() { - header "hardware report" - row " Cores" "$CORES" - - header "Nix config report" - row " Temp Dir" "$SCRATCH" - row " Nix Root" "$NIX_ROOT" - row " Build Users" "$NIX_USER_COUNT" - row " Build Group ID" "$NIX_BUILD_GROUP_ID" - row "Build Group Name" "$NIX_BUILD_GROUP_NAME" - if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" != "" ]; then - row "Preexisting Install" "Allowed" - fi - - subheader "build users:" - - row " Username" "UID" - for i in $(seq 1 "$NIX_USER_COUNT"); do - row " $(nix_user_for_core "$i")" "$(nix_uid_for_core "$i")" - done - echo "" +poly_user_exists() { + /usr/bin/dscl . -read "/Users/$1" > /dev/null 2>&1 } -create_build_group() { - local primary_group_id - - task "Setting up the build group $NIX_BUILD_GROUP_NAME" - if ! /usr/bin/dscl . -read "/Groups/$NIX_BUILD_GROUP_NAME" > /dev/null 2>&1; then - _sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \ - /usr/sbin/dseditgroup -o create \ - -r "Nix build group for nix-daemon" \ - -i "$NIX_BUILD_GROUP_ID" \ - "$NIX_BUILD_GROUP_NAME" >&2 - row " Created" "Yes" - else - primary_group_id=$(dsclattr "/Groups/$NIX_BUILD_GROUP_NAME" "PrimaryGroupID") - if [ "$primary_group_id" -ne "$NIX_BUILD_GROUP_ID" ]; then - failure <<EOF -It seems the build group $NIX_BUILD_GROUP_NAME already exists, but -with the UID $primary_group_id. This script can't really handle -that right now, so I'm going to give up. - -You can fix this by editing this script and changing the -NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID -to $primary_group_id and re-run. -EOF - else - row " Exists" "Yes" - fi - fi +poly_user_id_get() { + dsclattr "/Users/$1" "UniqueID" } -create_build_user_for_core() { - local coreid - local username - local uid - - coreid="$1" - username=$(nix_user_for_core "$coreid") - uid=$(nix_uid_for_core "$coreid") - dsclpath="/Users/$username" - - task "Setting up the build user $username" - - if ! /usr/bin/dscl . -read "$dsclpath" > /dev/null 2>&1; then - _sudo "Creating the Nix build user, $username" \ - /usr/bin/dscl . create "$dsclpath" \ - UniqueID "${uid}" - row " Created" "Yes" - else - actual_uid=$(dsclattr "$dsclpath" "UniqueID") - if [ "$actual_uid" -ne "$uid" ]; then - failure <<EOF -It seems the build user $username already exists, but with the UID -with the UID $actual_uid. This script can't really handle that right -now, so I'm going to give up. - -If you already created the users and you know they start from -$actual_uid and go up from there, you can edit this script and change -NIX_FIRST_BUILD_UID near the top of the file to $actual_uid and try -again. -EOF - else - row " Exists" "Yes" - fi - fi - - if [ "$(dsclattr "$dsclpath" "IsHidden")" = "1" ]; then - row " IsHidden" "Yes" - else - _sudo "in order to make $username a hidden user" \ - /usr/bin/dscl . -create "$dsclpath" "IsHidden" "1" - row " IsHidden" "Yes" - fi - - if [ "$(dsclattr "$dsclpath" "NFSHomeDirectory")" = "/var/empty" ]; then - row " NFSHomeDirectory" "/var/empty" - else - _sudo "in order to give $username a safe home directory" \ - /usr/bin/dscl . -create "$dsclpath" "NFSHomeDirectory" "/var/empty" - row " NFSHomeDirectory" "/var/empty" - fi - - if [ "$(dsclattr "$dsclpath" "RealName")" = "Nix build user $coreid" ]; then - row " RealName" "Nix build user $coreid" - else - _sudo "in order to give $username a useful name" \ - /usr/bin/dscl . -create "$dsclpath" "RealName" "Nix build user $coreid" - row " RealName" "Nix build user $coreid" - fi - - if [ "$(dsclattr "$dsclpath" "UserShell")" = "/sbin/nologin" ]; then - row " Logins Disabled" "Yes" - else - _sudo "in order to prevent $username from logging in" \ - /usr/bin/dscl . -create "$dsclpath" "UserShell" "/sbin/nologin" - row " Logins Disabled" "Yes" - fi - - if dseditgroup -o checkmember -m "$username" "$NIX_BUILD_GROUP_NAME" > /dev/null 2>&1 ; then - row " Member of $NIX_BUILD_GROUP_NAME" "Yes" - else - _sudo "Add $username to the $NIX_BUILD_GROUP_NAME group"\ - /usr/sbin/dseditgroup -o edit -t user \ - -a "$username" "$NIX_BUILD_GROUP_NAME" - row " Member of $NIX_BUILD_GROUP_NAME" "Yes" - fi - - if [ "$(dsclattr "$dsclpath" "PrimaryGroupID")" = "$NIX_BUILD_GROUP_ID" ]; then - row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID" - else - _sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \ - /usr/bin/dscl . -create "$dsclpath" "PrimaryGroupID" "$NIX_BUILD_GROUP_ID" - row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID" - - fi +poly_user_hidden_get() { + dsclattr "/Users/$1" "IsHidden" } -create_build_users() { - for i in $(seq 1 "$NIX_USER_COUNT"); do - create_build_user_for_core "$i" - done +poly_user_hidden_set() { + _sudo "in order to make $1 a hidden user" \ + /usr/bin/dscl . -create "/Users/$1" "IsHidden" "1" } -create_directories() { - _sudo "to make the basic directory structure of Nix (part 1)" \ - mkdir -pv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} - - _sudo "to make the basic directory structure of Nix (part 2)" \ - mkdir -pv -m 1777 /nix/var/nix/{gcroots,profiles}/per-user - - _sudo "to make the basic directory structure of Nix (part 3)" \ - mkdir -pv -m 1775 /nix/store - - _sudo "to make the basic directory structure of Nix (part 4)" \ - chgrp "$NIX_BUILD_GROUP_NAME" /nix/store - - _sudo "to set up the root user's profile (part 1)" \ - mkdir -pv -m 0755 /nix/var/nix/profiles/per-user/root - - _sudo "to set up the root user's profile (part 2)" \ - mkdir -pv -m 0700 "$ROOT_HOME/.nix-defexpr" - - _sudo "to place the default nix daemon configuration (part 1)" \ - mkdir -pv -m 0555 /etc/nix +poly_user_home_get() { + dsclattr "/Users/$1" "NFSHomeDirectory" } -place_channel_configuration() { - echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels" - _sudo "to set up the default system channel (part 1)" \ - install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels" +poly_user_home_set() { + _sudo "in order to give $1 a safe home directory" \ + /usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2" } -welcome_to_nix() { - ok "Welcome to the Multi-User Nix Installation" - - cat <<EOF - -This installation tool will set up your computer with the Nix package -manager. This will happen in a few stages: - -1. Make sure your computer doesn't already have Nix. If it does, I - will show you instructions on how to clean up your old one. - -2. Show you what we are going to install and where. Then we will ask - if you are ready to continue. - -3. Create the system users and groups that the Nix daemon uses to run - builds. - -4. Perform the basic installation of the Nix files daemon. - -5. Configure your shell to import special Nix Profile files, so you - can use Nix. - -6. Start the Nix daemon. - -EOF - - if ui_confirm "Would you like to see a more detailed list of what we will do?"; then - cat <<EOF - -We will: - - - make sure your computer doesn't already have Nix files - (if it does, I will tell you how to clean them up.) - - create local users (see the list above for the users we'll make) - - create a local group ($NIX_BUILD_GROUP_NAME) - - install Nix in to $NIX_ROOT - - create a configuration file in /etc/nix - - set up the "default profile" by creating some Nix-related files in - $ROOT_HOME -EOF - for profile_target in "${PROFILE_TARGETS[@]}"; do - if [ -e "$profile_target" ]; then - cat <<EOF - - back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX - - update $profile_target to include some Nix configuration -EOF - fi - done - cat <<EOF - - load and start a LaunchDaemon (at $PLIST_DEST) for nix-daemon - -EOF - if ! ui_confirm "Ready to continue?"; then - failure <<EOF -Okay, maybe you would like to talk to the team. -EOF - fi - fi +poly_user_note_get() { + dsclattr "/Users/$1" "RealName" } -chat_about_sudo() { - header "let's talk about sudo" - - if headless; then - cat <<EOF -This script is going to call sudo a lot. Normally, it would show you -exactly what commands it is running and why. However, the script is -run in a headless fashion, like this: - - $ curl https://nixos.org/nix/install | sh - -or maybe in a CI pipeline. Because of that, we're going to skip the -verbose output in the interest of brevity. - -If you would like to -see the output, try like this: - - $ curl -o install-nix https://nixos.org/nix/install - $ sh ./install-nix - -EOF - return 0 - fi - - cat <<EOF -This script is going to call sudo a lot. Every time we do, it'll -output exactly what it'll do, and why. - -Just like this: -EOF - - __sudo "to demonstrate how our sudo prompts look" \ - echo "this is a sudo prompt" - - cat <<EOF - -This might look scary, but everything can be undone by running just a -few commands. We used to ask you to confirm each time sudo ran, but it -was too many times. Instead, I'll just ask you this one time: - -EOF - if ui_confirm "Can we use sudo?"; then - ok "Yay! Thanks! Let's get going!" - else - failure <<EOF -That is okay, but we can't install. -EOF - fi +poly_user_note_set() { + _sudo "in order to give $username a useful note" \ + /usr/bin/dscl . -create "/Users/$1" "RealName" "$2" } -install_from_extracted_nix() { - ( - cd "$EXTRACTED_NIX_PATH" - - _sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \ - rsync -rlpt ./store/* "$NIX_ROOT/store/" - - if [ -d "$NIX_INSTALLED_NIX" ]; then - echo " Alright! We have our first nix at $NIX_INSTALLED_NIX" - else - failure <<EOF -Something went wrong, and I didn't find Nix installed at -$NIX_INSTALLED_NIX. -EOF - fi - - _sudo "to initialize the Nix Database" \ - $NIX_INSTALLED_NIX/bin/nix-store --init - - cat ./.reginfo \ - | _sudo "to load data for the first time in to the Nix Database" \ - "$NIX_INSTALLED_NIX/bin/nix-store" --load-db - - echo " Just finished getting the nix database ready." - ) +poly_user_shell_get() { + dsclattr "/Users/$1" "UserShell" } -shell_source_lines() { - cat <<EOF - -# Nix -if [ -e '$PROFILE_NIX_FILE' ]; then - . '$PROFILE_NIX_FILE' -fi -# End Nix - -EOF +poly_user_shell_set() { + _sudo "in order to give $1 a safe home directory" \ + /usr/bin/dscl . -create "/Users/$1" "UserShell" "$2" } -configure_shell_profile() { - for profile_target in "${PROFILE_TARGETS[@]}"; do - if [ -e "$profile_target" ]; then - _sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \ - cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX" - - shell_source_lines \ - | _sudo "extend your $profile_target with nix-daemon settings" \ - tee -a "$profile_target" - fi - done +poly_user_in_group_check() { + username=$1 + group=$2 + dseditgroup -o checkmember -m "$username" "$group" > /dev/null 2>&1 } -setup_default_profile() { - _sudo "to installing a bootstrapping Nix in to the default Profile" \ - HOME=$ROOT_HOME "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_NIX" - - _sudo "to installing a bootstrapping SSL certificate just for Nix in to the default Profile" \ - HOME=$ROOT_HOME "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_CACERT" +poly_user_in_group_set() { + username=$1 + group=$2 - _sudo "to update the default channel in the default profile" \ - HOME=$ROOT_HOME NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs + _sudo "Add $username to the $group group"\ + /usr/sbin/dseditgroup -o edit -t user \ + -a "$username" "$group" } - -place_nix_configuration() { - cat <<EOF > "$SCRATCH/nix.conf" -build-users-group = $NIX_BUILD_GROUP_NAME - -max-jobs = $NIX_USER_COUNT -cores = 1 -sandbox = false -EOF - _sudo "to place the default nix daemon configuration (part 2)" \ - install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf +poly_user_primary_group_get() { + dsclattr "/Users/$1" "PrimaryGroupID" } -configure_nix_daemon_plist() { - _sudo "to set up the nix-daemon as a LaunchDaemon" \ - ln -sfn "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST" - - _sudo "to load the LaunchDaemon plist for nix-daemon" \ - launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist - - _sudo "to start the nix-daemon" \ - launchctl start org.nixos.nix-daemon - +poly_user_primary_group_set() { + _sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \ + /usr/bin/dscl . -create "/Users/$1" "PrimaryGroupID" "$2" } +poly_create_build_user() { + username=$1 + uid=$2 + builder_num=$3 -main() { - welcome_to_nix - chat_about_sudo - - if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" = "" ]; then - validate_starting_assumptions - fi - - setup_report - - if ! ui_confirm "Ready to continue?"; then - ok "Alright, no changes have been made :)" - contactme - trap finish_cleanup EXIT - exit 1 - fi - - create_build_group - create_build_users - create_directories - place_channel_configuration - install_from_extracted_nix - - configure_shell_profile - - set +eu - . /etc/profile - set -eu - - setup_default_profile - place_nix_configuration - configure_nix_daemon_plist - - trap finish_success EXIT + _sudo "Creating the Nix build user (#$builder_num), $username" \ + /usr/bin/dscl . create "/Users/$username" \ + UniqueID "${uid}" } - - -main diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh new file mode 100644 index 000000000000..5f6542355e0c --- /dev/null +++ b/scripts/install-multi-user.sh @@ -0,0 +1,797 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +# Sourced from: +# - https://github.com/LnL7/nix-darwin/blob/8c29d0985d74b4a990238497c47a2542a5616b3c/bootstrap.sh +# - https://gist.github.com/expipiplus1/e571ce88c608a1e83547c918591b149f/ac504c6c1b96e65505fbda437a28ce563408ecb0 +# - https://github.com/NixOS/nixos-org-configurations/blob/a122f418797713d519aadf02e677fce0dc1cb446/delft/scripts/nix-mac-installer.sh +# - https://github.com/matthewbauer/macNixOS/blob/f6045394f9153edea417be90c216788e754feaba/install-macNixOS.sh +# - https://gist.github.com/LnL7/9717bd6cdcb30b086fd7f2093e5f8494/86b26f852ce563e973acd30f796a9a416248c34a +# +# however tracking which bits came from which would be impossible. + +readonly ESC='\033[0m' +readonly BOLD='\033[38;1m' +readonly BLUE='\033[38;34m' +readonly BLUE_UL='\033[38;4;34m' +readonly GREEN='\033[38;32m' +readonly GREEN_UL='\033[38;4;32m' +readonly RED='\033[38;31m' +readonly RED_UL='\033[38;4;31m' +readonly YELLOW='\033[38;33m' +readonly YELLOW_UL='\033[38;4;33m' + +readonly NIX_USER_COUNT="32" +readonly NIX_BUILD_GROUP_ID="30000" +readonly NIX_BUILD_GROUP_NAME="nixbld" +readonly NIX_FIRST_BUILD_UID="30001" +# Please don't change this. We don't support it, because the +# default shell profile that comes with Nix doesn't support it. +readonly NIX_ROOT="/nix" + +readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshrc") +readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix" +readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" + +readonly NIX_INSTALLED_NIX="@nix@" +readonly NIX_INSTALLED_CACERT="@cacert@" +readonly EXTRACTED_NIX_PATH="$(dirname "$0")" + +readonly ROOT_HOME=$(echo ~root) + +if [ -t 0 ]; then + readonly IS_HEADLESS='no' +else + readonly IS_HEADLESS='yes' +fi + +headless() { + if [ "$IS_HEADLESS" = "yes" ]; then + return 0 + else + return 1 + fi +} + +contactme() { + echo "We'd love to help if you need it." + echo "" + echo "If you can, open an issue at https://github.com/nixos/nix/issues" + echo "" + echo "Or feel free to contact the team," + echo " - on IRC #nixos on irc.freenode.net" + echo " - on twitter @nixos_org" +} + +uninstall_directions() { + subheader "Uninstalling nix:" + local step=0 + + if poly_service_installed_check; then + step=$((step + 1)) + poly_service_uninstall_directions "$step" + fi + + for profile_target in "${PROFILE_TARGETS[@]}"; do + if [ -e "$profile_target" ] && [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then + step=$((step + 1)) + cat <<EOF +$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target + + sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target + +(after this one, you may need to re-open any terminals that were +opened while it existed.) + +EOF + fi + done + + step=$((step + 1)) + cat <<EOF +$step. Delete the files Nix added to your system: + + sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels + +and that is it. + +EOF + +} + +nix_user_for_core() { + printf "nixbld%d" "$1" +} + +nix_uid_for_core() { + echo $((NIX_FIRST_BUILD_UID + $1 - 1)) +} + +_textout() { + echo -en "$1" + shift + if [ "$*" = "" ]; then + cat + else + echo "$@" + fi + echo -en "$ESC" +} + +header() { + follow="---------------------------------------------------------" + header=$(echo "---- $* $follow$follow$follow" | head -c 80) + echo "" + _textout "$BLUE" "$header" +} + +warningheader() { + follow="---------------------------------------------------------" + header=$(echo "---- $* $follow$follow$follow" | head -c 80) + echo "" + _textout "$RED" "$header" +} + +subheader() { + echo "" + _textout "$BLUE_UL" "$*" +} + +row() { + printf "$BOLD%s$ESC:\\t%s\\n" "$1" "$2" +} + +task() { + echo "" + ok "~~> $1" +} + +bold() { + echo "$BOLD$*$ESC" +} + +ok() { + _textout "$GREEN" "$@" +} + +warning() { + warningheader "warning!" + cat + echo "" +} + +failure() { + header "oh no!" + _textout "$RED" "$@" + echo "" + _textout "$RED" "$(contactme)" + trap finish_cleanup EXIT + exit 1 +} + +ui_confirm() { + _textout "$GREEN$GREEN_UL" "$1" + + if headless; then + echo "No TTY, assuming you would say yes :)" + return 0 + fi + + local prompt="[y/n] " + echo -n "$prompt" + while read -r y; do + if [ "$y" = "y" ]; then + echo "" + return 0 + elif [ "$y" = "n" ]; then + echo "" + return 1 + else + _textout "$RED" "Sorry, I didn't understand. I can only understand answers of y or n" + echo -n "$prompt" + fi + done + echo "" + return 1 +} + +__sudo() { + local expl="$1" + local cmd="$2" + shift + header "sudo execution" + + echo "I am executing:" + echo "" + printf " $ sudo %s\\n" "$cmd" + echo "" + echo "$expl" + echo "" + + return 0 +} + +_sudo() { + local expl="$1" + shift + if ! headless; then + __sudo "$expl" "$*" + fi + sudo "$@" +} + + +readonly SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX) +function finish_cleanup { + rm -rf "$SCRATCH" +} + +function finish_fail { + finish_cleanup + + failure <<EOF +Jeeze, something went wrong. If you can take all the output and open +an issue, we'd love to fix the problem so nobody else has this issue. + +:( +EOF +} +trap finish_fail EXIT + +function finish_success { + finish_cleanup + + ok "Alright! We're done!" + 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: + + $ nix-shell -p nix-info --run "nix-info -m" + +Thank you for using this installer. If you have any feedback, don't +hesitate: + +$(contactme) +EOF +} + + +validate_starting_assumptions() { + poly_validate_assumptions + + if [ $EUID -eq 0 ]; then + failure <<EOF +Please do not run this script with root privileges. We will call sudo +when we need to. +EOF + fi + + if type nix-env 2> /dev/null >&2; then + failure <<EOF +Nix already appears to be installed, and this tool assumes it is +_not_ yet installed. + +$(uninstall_directions) +EOF + fi + + if [ "${NIX_REMOTE:-}" != "" ]; then + failure <<EOF +For some reason, \$NIX_REMOTE is set. It really should not be set +before this installer runs, and it hints that Nix is currently +installed. Please delete the old Nix installation and start again. + +Note: You might need to close your shell window and open a new shell +to clear the variable. +EOF + fi + + if echo "${SSL_CERT_FILE:-}" | grep -qE "(nix/var/nix|nix-profile)"; then + failure <<EOF +It looks like \$SSL_CERT_FILE is set to a path that used to be part of +the old Nix installation. Please unset that variable and try again: + + $ unset SSL_CERT_FILE + +EOF + fi + + for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do + if [ -f "$file" ]; then + if grep -l "^[^#].*.nix-profile" "$file"; then + failure <<EOF +I found a reference to a ".nix-profile" in $file. +This has a high chance of breaking a new nix installation. It was most +likely put there by a previous Nix installer. + +Please remove this reference and try running this again. You should +also look for similar references in: + + - ~/.bash_profile + - ~/.bash_login + - ~/.profile + +or other shell init files that you may have. + +$(uninstall_directions) +EOF + fi + fi + done + + if [ -d /nix ]; then + failure <<EOF +There are some relics of a previous installation of Nix at /nix, and +this scripts assumes Nix is _not_ yet installed. Please delete the old +Nix installation and start again. + +$(uninstall_directions) +EOF + fi + + if [ -d /etc/nix ]; then + failure <<EOF +There are some relics of a previous installation of Nix at /etc/nix, and +this scripts assumes Nix is _not_ yet installed. Please delete the old +Nix installation and start again. + +$(uninstall_directions) +EOF + fi + + for profile_target in "${PROFILE_TARGETS[@]}"; do + if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then + failure <<EOF +When this script runs, it backs up the current $profile_target to +$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though. + +Please follow these instructions to clean up the old backup file: + +1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just +in case. + +2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like +it has anything nix-related in it. If it does, something is probably +quite wrong. Please open an issue or get in touch immediately. + +3. Take care to make sure that $profile_target doesn't look like it has +anything nix-related in it. If it does, and $profile_target _did not_, +run: + + $ /usr/bin/sudo /bin/mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target + +and try again. +EOF + fi + + if [ -e "$profile_target" ] && grep -qi "nix" "$profile_target"; then + failure <<EOF +It looks like $profile_target already has some Nix configuration in +there. There should be no reason to run this again. If you're having +trouble, please open an issue. +EOF + fi + done + + danger_paths=("$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.nix-profile") + for danger_path in "${danger_paths[@]}"; do + if _sudo "making sure that $danger_path doesn't exist" \ + test -e "$danger_path"; then + failure <<EOF +I found a file at $danger_path, which is a relic of a previous +installation. You must first delete this file before continuing. + +$(uninstall_directions) +EOF + fi + done +} + +setup_report() { + header "Nix config report" + row " Temp Dir" "$SCRATCH" + row " Nix Root" "$NIX_ROOT" + row " Build Users" "$NIX_USER_COUNT" + row " Build Group ID" "$NIX_BUILD_GROUP_ID" + row "Build Group Name" "$NIX_BUILD_GROUP_NAME" + if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" != "" ]; then + row "Preexisting Install" "Allowed" + fi + + subheader "build users:" + + row " Username" "UID" + for i in $(seq 1 "$NIX_USER_COUNT"); do + row " $(nix_user_for_core "$i")" "$(nix_uid_for_core "$i")" + done + echo "" +} + +create_build_group() { + local primary_group_id + + task "Setting up the build group $NIX_BUILD_GROUP_NAME" + if ! poly_group_exists "$NIX_BUILD_GROUP_NAME"; then + poly_create_build_group + row " Created" "Yes" + else + primary_group_id=$(poly_group_id_get "$NIX_BUILD_GROUP_NAME") + if [ "$primary_group_id" -ne "$NIX_BUILD_GROUP_ID" ]; then + failure <<EOF +It seems the build group $NIX_BUILD_GROUP_NAME already exists, but +with the UID $primary_group_id. This script can't really handle +that right now, so I'm going to give up. + +You can fix this by editing this script and changing the +NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID +to $primary_group_id and re-run. +EOF + else + row " Exists" "Yes" + fi + fi +} + +create_build_user_for_core() { + local coreid + local username + local uid + + coreid="$1" + username=$(nix_user_for_core "$coreid") + uid=$(nix_uid_for_core "$coreid") + + task "Setting up the build user $username" + + if ! poly_user_exists "$username"; then + poly_create_build_user "$username" "$uid" "$coreid" + row " Created" "Yes" + else + actual_uid=$(poly_user_id_get "$username") + if [ "$actual_uid" != "$uid" ]; then + failure <<EOF +It seems the build user $username already exists, but with the UID +with the UID '$actual_uid'. This script can't really handle that right +now, so I'm going to give up. + +If you already created the users and you know they start from +$actual_uid and go up from there, you can edit this script and change +NIX_FIRST_BUILD_UID near the top of the file to $actual_uid and try +again. +EOF + else + row " Exists" "Yes" + fi + fi + + if [ "$(poly_user_hidden_get "$username")" = "1" ]; then + row " Hidden" "Yes" + else + poly_user_hidden_set "$username" + row " Hidden" "Yes" + fi + + if [ "$(poly_user_home_get "$username")" = "/var/empty" ]; then + row " Home Directory" "/var/empty" + else + poly_user_home_set "$username" "/var/empty" + row " Home Directory" "/var/empty" + fi + + # We use grep instead of an equality check because it is difficult + # to extract _just_ the user's note, instead it is prefixed with + # some plist junk. This was causing the user note to always be set, + # even if there was no reason for it. + if ! poly_user_note_get "$username" | grep -q "Nix build user $coreid"; then + row " Note" "Nix build user $coreid" + else + poly_user_note_set "$username" "Nix build user $coreid" + row " Note" "Nix build user $coreid" + fi + + if [ "$(poly_user_shell_get "$username")" = "/sbin/nologin" ]; then + row " Logins Disabled" "Yes" + else + poly_user_shell_set "$username" "/sbin/nologin" + row " Logins Disabled" "Yes" + fi + + if poly_user_in_group_check "$username" "$NIX_BUILD_GROUP_NAME"; then + row " Member of $NIX_BUILD_GROUP_NAME" "Yes" + else + poly_user_in_group_set "$username" "$NIX_BUILD_GROUP_NAME" + row " Member of $NIX_BUILD_GROUP_NAME" "Yes" + fi + + if [ "$(poly_user_primary_group_get "$username")" = "$NIX_BUILD_GROUP_ID" ]; then + row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID" + else + poly_user_primary_group_set "$username" "$NIX_BUILD_GROUP_ID" + row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID" + fi +} + +create_build_users() { + for i in $(seq 1 "$NIX_USER_COUNT"); do + create_build_user_for_core "$i" + done +} + +create_directories() { + _sudo "to make the basic directory structure of Nix (part 1)" \ + mkdir -pv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} + + _sudo "to make the basic directory structure of Nix (part 2)" \ + mkdir -pv -m 1777 /nix/var/nix/{gcroots,profiles}/per-user + + _sudo "to make the basic directory structure of Nix (part 3)" \ + mkdir -pv -m 1775 /nix/store + + _sudo "to make the basic directory structure of Nix (part 4)" \ + chgrp "$NIX_BUILD_GROUP_NAME" /nix/store + + _sudo "to set up the root user's profile (part 1)" \ + mkdir -pv -m 0755 /nix/var/nix/profiles/per-user/root + + _sudo "to set up the root user's profile (part 2)" \ + mkdir -pv -m 0700 "$ROOT_HOME/.nix-defexpr" + + _sudo "to place the default nix daemon configuration (part 1)" \ + mkdir -pv -m 0555 /etc/nix +} + +place_channel_configuration() { + echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels" + _sudo "to set up the default system channel (part 1)" \ + install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels" +} + +welcome_to_nix() { + ok "Welcome to the Multi-User Nix Installation" + + cat <<EOF + +This installation tool will set up your computer with the Nix package +manager. This will happen in a few stages: + +1. Make sure your computer doesn't already have Nix. If it does, I + will show you instructions on how to clean up your old one. + +2. Show you what we are going to install and where. Then we will ask + if you are ready to continue. + +3. Create the system users and groups that the Nix daemon uses to run + builds. + +4. Perform the basic installation of the Nix files daemon. + +5. Configure your shell to import special Nix Profile files, so you + can use Nix. + +6. Start the Nix daemon. + +EOF + + if ui_confirm "Would you like to see a more detailed list of what we will do?"; then + cat <<EOF + +We will: + + - make sure your computer doesn't already have Nix files + (if it does, I will tell you how to clean them up.) + - create local users (see the list above for the users we'll make) + - create a local group ($NIX_BUILD_GROUP_NAME) + - install Nix in to $NIX_ROOT + - create a configuration file in /etc/nix + - set up the "default profile" by creating some Nix-related files in + $ROOT_HOME +EOF + for profile_target in "${PROFILE_TARGETS[@]}"; do + if [ -e "$profile_target" ]; then + cat <<EOF + - back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX + - update $profile_target to include some Nix configuration +EOF + fi + done + poly_service_setup_note + if ! ui_confirm "Ready to continue?"; then + failure <<EOF +Okay, maybe you would like to talk to the team. +EOF + fi + fi +} + +chat_about_sudo() { + header "let's talk about sudo" + + if headless; then + cat <<EOF +This script is going to call sudo a lot. Normally, it would show you +exactly what commands it is running and why. However, the script is +run in a headless fashion, like this: + + $ curl https://nixos.org/nix/install | sh + +or maybe in a CI pipeline. Because of that, we're going to skip the +verbose output in the interest of brevity. + +If you would like to +see the output, try like this: + + $ curl -o install-nix https://nixos.org/nix/install + $ sh ./install-nix + +EOF + return 0 + fi + + cat <<EOF +This script is going to call sudo a lot. Every time we do, it'll +output exactly what it'll do, and why. + +Just like this: +EOF + + __sudo "to demonstrate how our sudo prompts look" \ + echo "this is a sudo prompt" + + cat <<EOF + +This might look scary, but everything can be undone by running just a +few commands. We used to ask you to confirm each time sudo ran, but it +was too many times. Instead, I'll just ask you this one time: + +EOF + if ui_confirm "Can we use sudo?"; then + ok "Yay! Thanks! Let's get going!" + else + failure <<EOF +That is okay, but we can't install. +EOF + fi +} + +install_from_extracted_nix() { + ( + cd "$EXTRACTED_NIX_PATH" + + _sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \ + rsync -rlpt ./store/* "$NIX_ROOT/store/" + + if [ -d "$NIX_INSTALLED_NIX" ]; then + echo " Alright! We have our first nix at $NIX_INSTALLED_NIX" + else + failure <<EOF +Something went wrong, and I didn't find Nix installed at +$NIX_INSTALLED_NIX. +EOF + fi + + _sudo "to initialize the Nix Database" \ + $NIX_INSTALLED_NIX/bin/nix-store --init + + cat ./.reginfo \ + | _sudo "to load data for the first time in to the Nix Database" \ + "$NIX_INSTALLED_NIX/bin/nix-store" --load-db + + echo " Just finished getting the nix database ready." + ) +} + +shell_source_lines() { + cat <<EOF + +# Nix +if [ -e '$PROFILE_NIX_FILE' ]; then + . '$PROFILE_NIX_FILE' +fi +# End Nix + +EOF +} + +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 + if [ -e "$profile_target" ]; then + _sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \ + cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX" + + shell_source_lines \ + | _sudo "extend your $profile_target with nix-daemon settings" \ + tee -a "$profile_target" + fi + done +} + +setup_default_profile() { + _sudo "to installing a bootstrapping Nix in to the default Profile" \ + HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_NIX" + + _sudo "to installing a bootstrapping SSL certificate just for Nix in to the default Profile" \ + HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_CACERT" + + _sudo "to update the default channel in the default profile" \ + HOME="$ROOT_HOME" NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs +} + + +place_nix_configuration() { + cat <<EOF > "$SCRATCH/nix.conf" +build-users-group = $NIX_BUILD_GROUP_NAME + +max-jobs = $NIX_USER_COUNT +cores = 1 +sandbox = false +EOF + _sudo "to place the default nix daemon configuration (part 2)" \ + install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf +} + +main() { + if [ "$(uname -s)" = "Darwin" ]; then + # shellcheck source=./install-darwin-multi-user.sh + . "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh" + elif [ "$(uname -s)" = "Linux" ] && [ -e /run/systemd/system ]; then + # shellcheck source=./install-systemd-multi-user.sh + . "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh" + else + failure "Sorry, I don't know what to do on $(uname)" + fi + + welcome_to_nix + chat_about_sudo + + if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" = "" ]; then + validate_starting_assumptions + fi + + setup_report + + if ! ui_confirm "Ready to continue?"; then + ok "Alright, no changes have been made :)" + contactme + trap finish_cleanup EXIT + exit 1 + fi + + create_build_group + create_build_users + create_directories + place_channel_configuration + install_from_extracted_nix + + configure_shell_profile + + set +eu + . /etc/profile + set -eu + + setup_default_profile + place_nix_configuration + poly_configure_nix_daemon_service + + trap finish_success EXIT +} + + +main diff --git a/scripts/install-nix-from-closure.sh b/scripts/install-nix-from-closure.sh index 3e5676f419ba..cd71d7947d77 100644 --- a/scripts/install-nix-from-closure.sh +++ b/scripts/install-nix-from-closure.sh @@ -28,9 +28,41 @@ if [ "$(uname -s)" = "Darwin" ]; then echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.10 or higher" exit 1 fi +fi + +# Determine if we should punt to the single-user installer or not +if [ "$(uname -s)" = "Darwin" ]; then + INSTALL_MODE=daemon +elif [ "$(uname -s)" = "Linux" ] && [ -e /run/systemd/system ]; then + INSTALL_MODE=daemon +else + INSTALL_MODE=no-daemon +fi + +# Trivially handle the --daemon / --no-daemon options +if [ "x${1:-}" = "x--no-daemon" ]; then + INSTALL_MODE=no-daemon +elif [ "x${1:-}" = "x--daemon" ]; then + INSTALL_MODE=daemon +elif [ "x${1:-}" != "x" ]; then + ( + echo "Nix Installer [--daemon|--no-daemon]" + echo "" + echo " --daemon: Force the installer to use the Daemon" + echo " based installer, even though it may not" + echo " work." + echo "" + echo " --no-daemon: Force a no-daemon, single-user" + echo " installation even when the preferred" + echo " method is with the daemon." + echo "" + ) >&2 + exit +fi - printf '\e[1;31mSwitching to the Multi-User Darwin Installer\e[0m\n' - exec "$self/install-darwin-multi-user" +if [ "$INSTALL_MODE" = "daemon" ]; then + printf '\e[1;31mSwitching to the Daemon-based Installer\e[0m\n' + exec "$self/install-multi-user" exit 0 fi diff --git a/scripts/install-systemd-multi-user.sh b/scripts/install-systemd-multi-user.sh new file mode 100644 index 000000000000..04bc539a1099 --- /dev/null +++ b/scripts/install-systemd-multi-user.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service +readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service + +readonly SOCKET_SRC=/lib/systemd/system/nix-daemon.socket +readonly SOCKET_DEST=/etc/systemd/system/nix-daemon.socket + +poly_validate_assumptions() { + if [ "$(uname -s)" != "Linux" ]; then + failure "This script is for use with Linux!" + fi +} + +poly_service_installed_check() { + [ "$(systemctl is-enabled nix-daemon.service)" = "linked" ] \ + || [ "$(systemctl is-enabled nix-daemon.socket)" = "enabled" ] +} + +poly_service_uninstall_directions() { + cat <<EOF +$1. Delete the systemd service and socket units + + sudo systemctl stop nix-daemon.socket + sudo systemctl stop nix-daemon.service + sudo systemctl disable nix-daemon.socket + sudo systemctl disable nix-daemon.service + sudo systemctl daemon-reload +EOF +} + +poly_service_setup_note() { + cat <<EOF + - load and start a service (at $SERVICE_DEST + and $SOCKET_DEST) for nix-daemon + +EOF +} + +poly_configure_nix_daemon_service() { + _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" \ + systemctl enable "/nix/var/nix/profiles/default$SOCKET_SRC" + + _sudo "to load the systemd unit for nix-daemon" \ + systemctl daemon-reload + + _sudo "to start the nix-daemon.socket" \ + systemctl start nix-daemon.socket + + _sudo "to start the nix-daemon.service" \ + systemctl start nix-daemon.service + +} + +poly_group_exists() { + getent group "$1" > /dev/null 2>&1 +} + +poly_group_id_get() { + getent group "$1" | cut -d: -f3 +} + +poly_create_build_group() { + _sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \ + groupadd -g "$NIX_BUILD_GROUP_ID" --system \ + "$NIX_BUILD_GROUP_NAME" >&2 +} + +poly_user_exists() { + getent passwd "$1" > /dev/null 2>&1 +} + +poly_user_id_get() { + getent passwd "$1" | cut -d: -f3 +} + +poly_user_hidden_get() { + echo "1" +} + +poly_user_hidden_set() { + true +} + +poly_user_home_get() { + getent passwd "$1" | cut -d: -f6 +} + +poly_user_home_set() { + _sudo "in order to give $1 a safe home directory" \ + usermod --home "$2" "$1" +} + +poly_user_note_get() { + getent passwd "$1" | cut -d: -f5 +} + +poly_user_note_set() { + _sudo "in order to give $1 a useful comment" \ + usermod --comment "$2" "$1" +} + +poly_user_shell_get() { + getent passwd "$1" | cut -d: -f7 +} + +poly_user_shell_set() { + _sudo "in order to prevent $1 from logging in" \ + usermod --shell "$2" "$1" +} + +poly_user_in_group_check() { + groups "$1" | grep -q "$2" > /dev/null 2>&1 +} + +poly_user_in_group_set() { + _sudo "Add $1 to the $2 group"\ + usermod --append --groups "$2" "$1" +} + +poly_user_primary_group_get() { + getent passwd "$1" | cut -d: -f4 +} + +poly_user_primary_group_set() { + _sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \ + usermod --gid "$2" "$1" + +} + +poly_create_build_user() { + username=$1 + uid=$2 + builder_num=$3 + + _sudo "Creating the Nix build user, $username" \ + useradd \ + --home-dir /var/empty \ + --comment "Nix build user $builder_num" \ + --gid "$NIX_BUILD_GROUP_ID" \ + --groups "$NIX_BUILD_GROUP_NAME" \ + --no-user-group \ + --system \ + --shell /sbin/nologin \ + --uid "$uid" \ + --password "!" \ + "$username" +} diff --git a/src/build-remote/local.mk b/src/build-remote/local.mk index 64368a43ff73..50b0409d1886 100644 --- a/src/build-remote/local.mk +++ b/src/build-remote/local.mk @@ -4,6 +4,6 @@ build-remote_DIR := $(d) build-remote_INSTALL_DIR := $(libexecdir)/nix -build-remote_LIBS = libmain libutil libformat libstore +build-remote_LIBS = libmain libformat libstore libutil build-remote_SOURCES := $(d)/build-remote.cc diff --git a/src/libexpr/nix-expr.pc.in b/src/libexpr/nix-expr.pc.in index 21b6c38dd133..79f3e2f4506e 100644 --- a/src/libexpr/nix-expr.pc.in +++ b/src/libexpr/nix-expr.pc.in @@ -7,4 +7,4 @@ Description: Nix Package Manager Version: @PACKAGE_VERSION@ Requires: nix-store bdw-gc Libs: -L${libdir} -lnixexpr -Cflags: -I${includedir}/nix +Cflags: -I${includedir}/nix -std=c++14 diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c88f677da085..57dc7bd1279d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -155,7 +155,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v); /* Load a ValueInitializer from a DSO and return whatever it initializes */ -static void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v) +void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; Path path = state.coerceToPath(pos, *args[0], context); @@ -193,7 +193,7 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args /* Execute a program and parse its output */ -static void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) +void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceList(*args[0], pos); auto elems = args[0]->listElems(); @@ -271,7 +271,18 @@ static void prim_isNull(EvalState & state, const Pos & pos, Value * * args, Valu static void prim_isFunction(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0]); - mkBool(v, args[0]->type == tLambda); + bool res; + switch (args[0]->type) { + case tLambda: + case tPrimOp: + case tPrimOpApp: + res = true; + break; + default: + res = false; + break; + } + mkBool(v, res); } diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh index 31bf3f84f6c7..c790b30f6d0b 100644 --- a/src/libexpr/primops.hh +++ b/src/libexpr/primops.hh @@ -15,4 +15,12 @@ struct RegisterPrimOp RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun); }; +/* These primops are disabled without enableNativeCode, but plugins + may wish to use them in limited contexts without globally enabling + them. */ +/* Load a ValueInitializer from a DSO and return whatever it initializes */ +void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v); +/* Execute a program and parse its output */ +void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v); + } diff --git a/src/libmain/nix-main.pc.in b/src/libmain/nix-main.pc.in index de1bdf706f72..38bc85c484eb 100644 --- a/src/libmain/nix-main.pc.in +++ b/src/libmain/nix-main.pc.in @@ -6,4 +6,4 @@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixmain -Cflags: -I${includedir}/nix +Cflags: -I${includedir}/nix -std=c++14 diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 73139d6d551a..416c775a35d2 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -964,6 +964,8 @@ private: } void done(BuildResult::Status status, const string & msg = ""); + + PathSet exportReferences(PathSet storePaths); }; @@ -1730,22 +1732,23 @@ int childEntry(void * arg) } -PathSet exportReferences(Store & store, PathSet storePaths) +PathSet DerivationGoal::exportReferences(PathSet storePaths) { PathSet paths; for (auto storePath : storePaths) { /* Check that the store path is valid. */ - if (!store.isInStore(storePath)) + if (!worker.store.isInStore(storePath)) throw BuildError(format("'exportReferencesGraph' contains a non-store path '%1%'") % storePath); - storePath = store.toStorePath(storePath); - if (!store.isValidPath(storePath)) - throw BuildError(format("'exportReferencesGraph' contains an invalid path '%1%'") - % storePath); - store.computeFSClosure(storePath, paths); + storePath = worker.store.toStorePath(storePath); + + if (!inputPaths.count(storePath)) + throw BuildError("cannot export references of path '%s' because it is not in the input closure of the derivation", storePath); + + worker.store.computeFSClosure(storePath, paths); } /* If there are derivations in the graph, then include their @@ -1756,9 +1759,9 @@ PathSet exportReferences(Store & store, PathSet storePaths) for (auto & j : paths2) { if (isDerivation(j)) { - Derivation drv = store.derivationFromPath(j); + Derivation drv = worker.store.derivationFromPath(j); for (auto & k : drv.outputs) - store.computeFSClosure(k.second.path, paths); + worker.store.computeFSClosure(k.second.path, paths); } } @@ -1882,7 +1885,7 @@ void DerivationGoal::startBuilder() /* Write closure info to <fileName>. */ writeFile(tmpDir + "/" + fileName, worker.store.makeValidityRegistration( - exportReferences(worker.store, {storePath}), false, false)); + exportReferences({storePath}), false, false)); } } @@ -2384,7 +2387,7 @@ void DerivationGoal::writeStructuredAttrs() for (auto & p : *i) storePaths.insert(p.get<std::string>()); worker.store.pathInfoToJSON(jsonRoot, - exportReferences(worker.store, storePaths), false, true); + exportReferences(storePaths), false, true); } json[i.key()] = nlohmann::json::parse(str.str()); // urgh } @@ -2696,8 +2699,8 @@ void DerivationGoal::runChild() } else { if (errno != EINVAL) throw SysError("mounting /dev/pts"); - doBind("/dev/pts", "/dev/pts"); - doBind("/dev/ptmx", "/dev/ptmx"); + doBind("/dev/pts", chrootRootDir + "/dev/pts"); + doBind("/dev/ptmx", chrootRootDir + "/dev/ptmx"); } } @@ -3687,7 +3690,7 @@ void SubstitutionGoal::tryNext() only after we've downloaded the path. */ if (worker.store.requireSigs && !sub->isTrusted - && !info->checkSignatures(worker.store, worker.store.publicKeys)) + && !info->checkSignatures(worker.store, worker.store.getPublicKeys())) { printError("warning: substituter '%s' does not have a valid signature for path '%s'", sub->getUri(), storePath); diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 938d02c35a02..74e706664694 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -9,14 +9,6 @@ namespace nix { typedef std::map<Path,int> Priorities; -static bool isDirectory(const Path & path) -{ - struct stat st; - if (stat(path.c_str(), &st) == -1) - throw SysError(format("getting status of '%1%'") % path); - return S_ISDIR(st.st_mode); -} - // FIXME: change into local variables. static Priorities priorities; @@ -26,14 +18,37 @@ static unsigned long symlinks; /* For each activated package, create symlinks */ static void createLinks(const Path & srcDir, const Path & dstDir, int priority) { - auto srcFiles = readDirectory(srcDir); + DirEntries srcFiles; + + try { + srcFiles = readDirectory(srcDir); + } catch (SysError & e) { + if (e.errNo == ENOTDIR) { + printError("warning: not including '%s' in the user environment because it's not a directory", srcDir); + return; + } + throw; + } + for (const auto & ent : srcFiles) { if (ent.name[0] == '.') /* not matched by glob */ continue; - const auto & srcFile = srcDir + "/" + ent.name; + auto srcFile = srcDir + "/" + ent.name; auto dstFile = dstDir + "/" + ent.name; + struct stat srcSt; + try { + if (stat(srcFile.c_str(), &srcSt) == -1) + throw SysError("getting status of '%1%'", srcFile); + } catch (SysError & e) { + if (e.errNo == ENOENT || e.errNo == ENOTDIR) { + printError("warning: skipping dangling symlink '%s'", dstFile); + continue; + } + throw; + } + /* The files below are special-cased to that they don't show up * in user profiles, either because they are useless, or * because they would cauase pointless collisions (e.g., each @@ -44,9 +59,10 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority) hasSuffix(srcFile, "/nix-support") || hasSuffix(srcFile, "/perllocal.pod") || hasSuffix(srcFile, "/info/dir") || - hasSuffix(srcFile, "/log")) { + hasSuffix(srcFile, "/log")) continue; - } else if (isDirectory(srcFile)) { + + else if (S_ISDIR(srcSt.st_mode)) { struct stat dstSt; auto res = lstat(dstFile.c_str(), &dstSt); if (res == 0) { @@ -54,10 +70,9 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority) createLinks(srcFile, dstFile, priority); continue; } else if (S_ISLNK(dstSt.st_mode)) { - auto target = readLink(dstFile); - if (!isDirectory(target)) - throw Error(format("collision between '%1%' and non-directory '%2%'") - % srcFile % target); + auto target = canonPath(dstFile, true); + if (!S_ISDIR(lstat(target).st_mode)) + throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target); if (unlink(dstFile.c_str()) == -1) throw SysError(format("unlinking '%1%'") % dstFile); if (mkdir(dstFile.c_str(), 0755) == -1) @@ -68,28 +83,31 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority) } } else if (errno != ENOENT) throw SysError(format("getting status of '%1%'") % dstFile); - } else { + } + + else { struct stat dstSt; auto res = lstat(dstFile.c_str(), &dstSt); if (res == 0) { if (S_ISLNK(dstSt.st_mode)) { - auto target = readLink(dstFile); auto prevPriority = priorities[dstFile]; if (prevPriority == priority) - throw Error(format( + throw Error( "packages '%1%' and '%2%' have the same priority %3%; " "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' " "to change the priority of one of the conflicting packages" - " (0 being the highest priority)" - ) % srcFile % target % priority); + " (0 being the highest priority)", + srcFile, readLink(dstFile), priority); if (prevPriority < priority) continue; if (unlink(dstFile.c_str()) == -1) throw SysError(format("unlinking '%1%'") % dstFile); - } + } else if (S_ISDIR(dstSt.st_mode)) + throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile); } else if (errno != ENOENT) throw SysError(format("getting status of '%1%'") % dstFile); } + createSymlink(srcFile, dstFile); priorities[dstFile] = priority; symlinks++; @@ -105,24 +123,18 @@ static Path out; static void addPkg(const Path & pkgDir, int priority) { - if (done.find(pkgDir) != done.end()) - return; + if (done.count(pkgDir)) return; done.insert(pkgDir); createLinks(pkgDir, out, priority); - auto propagatedFN = pkgDir + "/nix-support/propagated-user-env-packages"; - std::string propagated; - { - AutoCloseFD fd = open(propagatedFN.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) { - if (errno == ENOENT) - return; - throw SysError(format("opening '%1%'") % propagatedFN); - } - propagated = readFile(fd.get()); + + try { + for (const auto & p : tokenizeString<std::vector<string>>( + readFile(pkgDir + "/nix-support/propagated-user-env-packages"), " \n")) + if (!done.count(p)) + postponed.insert(p); + } catch (SysError & e) { + if (e.errNo != ENOENT && e.errNo != ENOTDIR) throw; } - for (const auto & p : tokenizeString<std::vector<string>>(propagated, " \n")) - if (done.find(p) == done.end()) - postponed.insert(p); } struct Package { @@ -190,4 +202,3 @@ void builtinBuildenv(const BasicDerivation & drv) } } - diff --git a/src/libstore/download.cc b/src/libstore/download.cc index 4d7f5690192d..18f9094f82e0 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -173,7 +173,11 @@ struct CurlDownloader : public Downloader int progressCallback(double dltotal, double dlnow) { - act.progress(dlnow, dltotal); + try { + act.progress(dlnow, dltotal); + } catch (nix::Interrupted &) { + assert(_isInterrupted); + } return _isInterrupted; } @@ -730,8 +734,8 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa Hash gotHash = unpack ? hashPath(expectedHash.type, store->toRealPath(storePath)).first : hashFile(expectedHash.type, store->toRealPath(storePath)); - throw nix::Error("hash mismatch in file downloaded from '%s': expected hash '%s', got '%s'", - url, expectedHash.to_string(), gotHash.to_string()); + throw nix::Error("hash mismatch in file downloaded from '%s': got hash '%s' instead of the expected hash '%s'", + url, gotHash.to_string(), expectedHash.to_string()); } return store->toRealPath(storePath); diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index f46e8326235f..544566e0b573 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -159,7 +159,7 @@ void initPlugins() void *handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) - throw Error("could not dynamically open plugin file '%s%': %s%", file, dlerror()); + throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); } } /* We handle settings registrations here, since plugins can add settings */ diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 117404ec14c8..7430bbedbe44 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -313,6 +313,14 @@ public: Setting<Strings> trustedUsers{this, {"root"}, "trusted-users", "Which users or groups are trusted to ask the daemon to do unsafe things."}; + Setting<unsigned int> ttlNegativeNarInfoCache{this, 3600, "narinfo-cache-negative-ttl", + "The TTL in seconds for negative lookups in the disk cache i.e binary cache lookups that " + "return an invalid path result"}; + + Setting<unsigned int> ttlPositiveNarInfoCache{this, 30 * 24 * 3600, "narinfo-cache-positive-ttl", + "The TTL in seconds for positive lookups in the disk cache i.e binary cache lookups that " + "return a valid path result."}; + /* ?Who we trust to use the daemon in safe ways */ Setting<Strings> allowedUsers{this, {"*"}, "allowed-users", "Which users or groups are allowed to connect to the daemon."}; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index acc0002acee1..b63584f28a30 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -53,7 +53,6 @@ LocalStore::LocalStore(const Params & params) , trashDir(realStoreDir + "/trash") , tempRootsDir(stateDir + "/temproots") , fnTempRoots(fmt("%s/%d", tempRootsDir, getpid())) - , publicKeys(getDefaultPublicKeys()) { auto state(_state.lock()); @@ -964,12 +963,21 @@ void LocalStore::invalidatePath(State & state, const Path & path) } +const PublicKeys & LocalStore::getPublicKeys() +{ + auto state(_state.lock()); + if (!state->publicKeys) + state->publicKeys = std::make_unique<PublicKeys>(getDefaultPublicKeys()); + return *state->publicKeys; +} + + void LocalStore::addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) { assert(info.narHash); - if (requireSigs && checkSigs && !info.checkSignatures(*this, publicKeys)) + if (requireSigs && checkSigs && !info.checkSignatures(*this, getPublicKeys())) throw Error("cannot add path '%s' because it lacks a valid signature", info.path); addTempRoot(info.path); diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 0d6c176595c8..1209a06356f7 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -77,6 +77,8 @@ private: minFree but not much below availAfterGC, then there is no point in starting a new GC. */ uint64_t availAfterGC = std::numeric_limits<uint64_t>::max(); + + std::unique_ptr<PublicKeys> publicKeys; }; Sync<State, std::recursive_mutex> _state; @@ -100,7 +102,7 @@ private: settings.requireSigs, "require-sigs", "whether store paths should have a trusted signature on import"}; - PublicKeys publicKeys; + const PublicKeys & getPublicKeys(); public: diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 3c52303f0ea4..35403e5df56f 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -1,6 +1,7 @@ #include "nar-info-disk-cache.hh" #include "sync.hh" #include "sqlite.hh" +#include "globals.hh" #include <sqlite3.h> @@ -47,10 +48,6 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache { public: - /* How long negative and positive lookups are valid. */ - const int ttlNegative = 3600; - const int ttlPositive = 30 * 24 * 3600; - /* How often to purge expired entries from the cache. */ const int purgeInterval = 24 * 3600; @@ -116,8 +113,8 @@ public: SQLiteStmt(state->db, "delete from NARs where ((present = 0 and timestamp < ?) or (present = 1 and timestamp < ?))") .use() - (now - ttlNegative) - (now - ttlPositive) + (now - settings.ttlNegativeNarInfoCache) + (now - settings.ttlPositiveNarInfoCache) .exec(); debug("deleted %d entries from the NAR info disk cache", sqlite3_changes(state->db)); @@ -186,8 +183,8 @@ public: auto queryNAR(state->queryNAR.use() (cache.id) (hashPart) - (now - ttlNegative) - (now - ttlPositive)); + (now - settings.ttlNegativeNarInfoCache) + (now - settings.ttlPositiveNarInfoCache)); if (!queryNAR.next()) return {oUnknown, 0}; diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in index 3f1a2d83d2f2..5cf22faadcbe 100644 --- a/src/libstore/nix-store.pc.in +++ b/src/libstore/nix-store.pc.in @@ -5,5 +5,5 @@ includedir=@includedir@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnixstore -lnixutil -lnixformat -Cflags: -I${includedir}/nix +Libs: -L${libdir} -lnixstore -lnixutil +Cflags: -I${includedir}/nix -std=c++14 diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index cf628519c6e7..a63b3e07ae77 100755 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -212,7 +212,7 @@ void mainWrapped(int argc, char * * argv) // read the shebang to understand which packages to read from. Since // this is handled via nix-shell -p, we wrap our ruby script execution // in ruby -e 'load' which ignores the shebangs. - envCommand = (format("exec %1% %2% -e 'load(\"%3%\") -- %4%") % execArgs % interpreter % script % joined.str()).str(); + envCommand = (format("exec %1% %2% -e 'load(\"%3%\")' -- %4%") % execArgs % interpreter % script % joined.str()).str(); } else { envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % script % joined.str()).str(); } diff --git a/src/nix-channel/local.mk b/src/nix-channel/local.mk index 49fc105c6f79..c14e8c359ca0 100644 --- a/src/nix-channel/local.mk +++ b/src/nix-channel/local.mk @@ -2,6 +2,6 @@ programs += nix-channel nix-channel_DIR := $(d) -nix-channel_LIBS = libmain libutil libformat libstore +nix-channel_LIBS = libmain libformat libstore libutil nix-channel_SOURCES := $(d)/nix-channel.cc diff --git a/src/nix-copy-closure/local.mk b/src/nix-copy-closure/local.mk index 42bb34dd8201..5018ab975b44 100644 --- a/src/nix-copy-closure/local.mk +++ b/src/nix-copy-closure/local.mk @@ -2,6 +2,6 @@ programs += nix-copy-closure nix-copy-closure_DIR := $(d) -nix-copy-closure_LIBS = libmain libutil libformat libstore +nix-copy-closure_LIBS = libmain libformat libstore libutil nix-copy-closure_SOURCES := $(d)/nix-copy-closure.cc diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc index f28a52b73ff9..35603af7082a 100644 --- a/src/nix-daemon/nix-daemon.cc +++ b/src/nix-daemon/nix-daemon.cc @@ -1035,7 +1035,7 @@ static void daemonLoop(char * * argv) }, options); } catch (Interrupted & e) { - throw; + return; } catch (Error & e) { printError(format("error processing connection: %1%") % e.msg()); } diff --git a/src/nix/edit.cc b/src/nix/edit.cc index 7eaa86e2f914..c9671f76d0fa 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -61,7 +61,7 @@ struct CmdEdit : InstallableCommand auto editor = getEnv("EDITOR", "cat"); - Strings args{editor}; + auto args = tokenizeString<Strings>(editor); if (editor.find("emacs") != std::string::npos || editor.find("nano") != std::string::npos || @@ -72,7 +72,7 @@ struct CmdEdit : InstallableCommand stopProgressBar(); - execvp(editor.c_str(), stringsToCharPtrs(args).data()); + execvp(args.front().c_str(), stringsToCharPtrs(args).data()); throw SysError("cannot run editor '%s'", editor); } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 9216209173d9..f84774a53367 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -189,6 +189,7 @@ bool NixRepl::getLine(string & input, const std::string &prompt) if (!s) { switch (auto type = linenoiseKeyType()) { case 1: // ctrl-C + input = ""; return true; case 2: // ctrl-D return false; @@ -197,6 +198,7 @@ bool NixRepl::getLine(string & input, const std::string &prompt) } } input += s; + input += '\n'; return true; } diff --git a/src/nix/search.cc b/src/nix/search.cc index 5ccf1b7cf529..539676698086 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -25,14 +25,14 @@ std::string hilite(const std::string & s, const std::smatch & m) struct CmdSearch : SourceExprCommand, MixJSON { - std::string re; + std::vector<std::string> res; bool writeCache = true; bool useCache = true; CmdSearch() { - expectArg("regex", &re, true); + expectArgs("regex", &res); mkFlag() .longName("update-cache") @@ -68,9 +68,13 @@ struct CmdSearch : SourceExprCommand, MixJSON "nix search blender" }, Example{ - "To search for Firefox and Chromium:", + "To search for Firefox or Chromium:", "nix search 'firefox|chromium'" }, + Example{ + "To search for git and frontend or gui:", + "nix search git 'frontend|gui'" + }, }; } @@ -81,9 +85,16 @@ struct CmdSearch : SourceExprCommand, MixJSON // Empty search string should match all packages // Use "^" here instead of ".*" due to differences in resulting highlighting // (see #1893 -- libc++ claims empty search string is not in POSIX grammar) - if (re.empty()) re = "^"; + if (res.empty()) { + res.push_back("^"); + } - std::regex regex(re, std::regex::extended | std::regex::icase); + std::vector<std::regex> regexes; + regexes.reserve(res.size()); + + for (auto &re : res) { + regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase)); + } auto state = getEvalState(); @@ -102,6 +113,7 @@ struct CmdSearch : SourceExprCommand, MixJSON debug("at attribute '%s'", attrPath); try { + uint found = 0; state->forceValue(*v); @@ -115,25 +127,33 @@ struct CmdSearch : SourceExprCommand, MixJSON if (state->isDerivation(*v)) { DrvInfo drv(*state, attrPath, v->attrs); + std::string description; + std::smatch attrPathMatch; + std::smatch descriptionMatch; + std::smatch nameMatch; + std::string name; DrvName parsed(drv.queryName()); - std::smatch attrPathMatch; - std::regex_search(attrPath, attrPathMatch, regex); + for (auto ®ex : regexes) { + std::regex_search(attrPath, attrPathMatch, regex); - auto name = parsed.name; - std::smatch nameMatch; - std::regex_search(name, nameMatch, regex); + name = parsed.name; + std::regex_search(name, nameMatch, regex); - std::string description = drv.queryMetaString("description"); - std::replace(description.begin(), description.end(), '\n', ' '); - std::smatch descriptionMatch; - std::regex_search(description, descriptionMatch, regex); + description = drv.queryMetaString("description"); + std::replace(description.begin(), description.end(), '\n', ' '); + std::regex_search(description, descriptionMatch, regex); + + if (!attrPathMatch.empty() + || !nameMatch.empty() + || !descriptionMatch.empty()) + { + found++; + } + } - if (!attrPathMatch.empty() - || !nameMatch.empty() - || !descriptionMatch.empty()) - { + if (found == res.size()) { if (json) { auto jsonElem = jsonOut->object(attrPath); diff --git a/tests/lang/eval-okay-regex-split.exp b/tests/lang/eval-okay-regex-split.exp new file mode 100644 index 000000000000..27ba77ddaf61 --- /dev/null +++ b/tests/lang/eval-okay-regex-split.exp @@ -0,0 +1 @@ +true diff --git a/tests/nix-shell.sh b/tests/nix-shell.sh index 063e97ce2c75..d25c456cedfb 100644 --- a/tests/nix-shell.sh +++ b/tests/nix-shell.sh @@ -39,3 +39,12 @@ chmod a+rx $TEST_ROOT/shell.shebang.sh output=$($TEST_ROOT/shell.shebang.sh abc def) [ "$output" = "foo bar abc def" ] + +# Test nix-shell shebang mode for ruby +# This uses a fake interpreter that returns the arguments passed +# This, in turn, verifies the `rc` script is valid and the `load()` script (given using `-e`) is as expected. +sed -e "s|@SHELL_PROG@|$(type -p nix-shell)|" shell.shebang.rb > $TEST_ROOT/shell.shebang.rb +chmod a+rx $TEST_ROOT/shell.shebang.rb + +output=$($TEST_ROOT/shell.shebang.rb abc ruby) +[ "$output" = '-e load("'"$TEST_ROOT"'/shell.shebang.rb") -- abc ruby' ] diff --git a/tests/search.sh b/tests/search.sh index d83427247d3e..0b26a125120f 100644 --- a/tests/search.sh +++ b/tests/search.sh @@ -29,6 +29,11 @@ clearCache # Check search that matches nothing (( $(nix search nosuchpackageexists | wc -l) == 0 )) +# Search for multiple arguments +(( $(nix search hello empty | wc -l) == 5 )) + +# Multiple arguments will not exist +(( $(nix search hello broken | wc -l) == 0 )) ## Search expressions diff --git a/tests/shell.nix b/tests/shell.nix index 5845d36fc161..eb39f9039a88 100644 --- a/tests/shell.nix +++ b/tests/shell.nix @@ -45,5 +45,12 @@ let pkgs = rec { bash = shell; + # ruby "interpreter" that outputs "$@" + ruby = runCommand "ruby" {} '' + mkdir -p $out/bin + echo 'printf -- "$*"' > $out/bin/ruby + chmod a+rx $out/bin/ruby + ''; + inherit pkgs; }; in pkgs diff --git a/tests/shell.shebang.rb b/tests/shell.shebang.rb new file mode 100644 index 000000000000..ea67eb09c1c6 --- /dev/null +++ b/tests/shell.shebang.rb @@ -0,0 +1,7 @@ +#! @SHELL_PROG@ +#! ruby +#! nix-shell -I nixpkgs=shell.nix --no-substitute +#! nix-shell --pure -p ruby -i ruby + +# Contents doesn't matter. +abort("This shouldn't be executed.") diff --git a/version b/version index 415b19fc3623..42f7d2336ea8 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.0 \ No newline at end of file +2.1 \ No newline at end of file |